home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Games Extra 1996 September
/
Amiga Games Extra CD-ROM 9-1996.iso
/
userbox
/
publicdomain
/
vim-4.2
/
src
/
cmdline.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-06-17
|
125KB
|
5,383 lines
/* vi:set ts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
*/
/*
* cmdline.c: functions for reading in the command line and executing it
*/
#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"
#include "cmdtab.h"
#include "ops.h" /* included because we call functions in ops.c */
#ifdef HAVE_FCNTL_H
# include <fcntl.h> /* for chdir() */
#endif
/*
* variables shared between getcmdline() and redrawcmdline()
*/
static char_u *cmdbuff; /* pointer to command line buffer */
static int cmdbufflen; /* length of cmdbuff */
static int cmdlen; /* number of chars on command line */
static int cmdpos; /* current cursor position */
static int cmdspos; /* cursor column on screen */
static int cmdfirstc; /* ':', '/' or '?' */
/*
* Typing mode on the command line. Shared by getcmdline() and
* put_on_cmdline().
*/
static int overstrike = FALSE; /* typing mode */
/*
* The next two variables contain the bounds of any range given in a command.
* They are set by do_cmdline().
*/
static linenr_t line1, line2;
static int forceit;
static int regname;
static int quitmore = 0;
static int cmd_numfiles = -1; /* number of files found by
filename completion */
/*
* There are two history tables:
* 0: colon commands
* 1: search commands
*/
static char_u **(history[2]) = {NULL, NULL}; /* history tables */
static int hisidx[2] = {-1, -1}; /* last entered entry */
static int hislen = 0; /* actual lengt of history tables */
#ifdef RIGHTLEFT
static int cmd_hkmap = 0; /* Hebrew mapping during command line */
#endif
static void init_history __ARGS((void));
static int is_in_history __ARGS((int, char_u *, int));
static void putcmdline __ARGS((int));
static void redrawcmd __ARGS((void));
static void cursorcmd __ARGS((void));
static int ccheck_abbr __ARGS((int));
static char_u *do_one_cmd __ARGS((char_u **, int *, int));
static int buf_write_all __ARGS((BUF *));
static int do_write __ARGS((char_u *, int));
static char_u *getargcmd __ARGS((char_u **));
static void backslash_halve __ARGS((char_u *p, int expand_wildcards));
static void do_make __ARGS((char_u *));
static int do_arglist __ARGS((char_u *));
static int is_backslash __ARGS((char_u *str));
static int check_readonly __ARGS((void));
static int check_changed __ARGS((BUF *, int, int));
static int check_changed_any __ARGS((void));
static int check_more __ARGS((int));
static void vim_strncpy __ARGS((char_u *, char_u *, int));
static int nextwild __ARGS((int));
static int showmatches __ARGS((char_u *));
static linenr_t get_address __ARGS((char_u **));
static void set_expand_context __ARGS((int, char_u *));
static char_u *set_one_cmd_context __ARGS((int, char_u *));
static int ExpandFromContext __ARGS((char_u *, int *, char_u ***, int, int));
static int ExpandCommands __ARGS((regexp *, int *, char_u ***));
/*
* init_history() - initialize the command line history
*/
static void
init_history()
{
int newlen; /* new length of history table */
char_u **temp;
register int i;
int j;
int type;
/*
* If size of history table changed, reallocate it
*/
newlen = (int)p_hi;
if (newlen != hislen) /* history length changed */
{
for (type = 0; type <= 1; ++type) /* adjust both history tables */
{
if (newlen)
temp = (char_u **)lalloc((long_u)(newlen * sizeof(char_u *)),
TRUE);
else
temp = NULL;
if (newlen == 0 || temp != NULL)
{
if (hisidx[type] < 0) /* there are no entries yet */
{
for (i = 0; i < newlen; ++i)
temp[i] = NULL;
}
else if (newlen > hislen) /* array becomes bigger */
{
for (i = 0; i <= hisidx[type]; ++i)
temp[i] = history[type][i];
j = i;
for ( ; i <= newlen - (hislen - hisidx[type]); ++i)
temp[i] = NULL;
for ( ; j < hislen; ++i, ++j)
temp[i] = history[type][j];
}
else /* array becomes smaller or 0 */
{
j = hisidx[type];
for (i = newlen - 1; ; --i)
{
if (i >= 0) /* copy newest entries */
temp[i] = history[type][j];
else /* remove older entries */
vim_free(history[type][j]);
if (--j < 0)
j = hislen - 1;
if (j == hisidx[type])
break;
}
hisidx[type] = newlen - 1;
}
vim_free(history[type]);
history[type] = temp;
}
}
hislen = newlen;
}
}
/*
* check if command line 'str' is already in history
* 'type' is 0 for ':' commands, '1' for search commands
* if 'move_to_front' is TRUE, matching entry is moved to end of history
*/
static int
is_in_history(type, str, move_to_front)
int type;
char_u *str;
int move_to_front; /* Move the entry to the front if it exists */
{
int i;
int last_i = -1;
if (hisidx[type] < 0)
return FALSE;
i = hisidx[type];
do
{
if (history[type][i] == NULL)
return FALSE;
if (STRCMP(str, history[type][i]) == 0)
{
if (!move_to_front)
return TRUE;
last_i = i;
break;
}
if (--i < 0)
i = hislen - 1;
} while (i != hisidx[type]);
if (last_i >= 0)
{
str = history[type][i];
while (i != hisidx[type])
{
if (++i >= hislen)
i = 0;
history[type][last_i] = history[type][i];
last_i = i;
}
history[type][i] = str;
return TRUE;
}
return FALSE;
}
/*
* Add the given string to the given history. If the string is already in the
* history then it is moved to the front. histype may be 0 for the ':'
* history, or 1 for the '/' history.
*/
void
add_to_history(histype, new_entry)
int histype;
char_u *new_entry;
{
if (hislen != 0 && !is_in_history(histype, new_entry, TRUE))
{
if (++hisidx[histype] == hislen)
hisidx[histype] = 0;
vim_free(history[histype][hisidx[histype]]);
history[histype][hisidx[histype]] = strsave(new_entry);
}
}
/*
* getcmdline() - accept a command line starting with ':', '/', or '?'
*
* The line is collected in cmdbuff, which is reallocated to fit the command
* line.
*
* Return pointer to allocated string if there is a commandline, NULL
* otherwise.
*/
char_u *
getcmdline(firstc, count)
int firstc; /* either ':', '/', or '?' */
long count; /* only used for incremental search */
{
register int c;
#ifdef DIGRAPHS
int cc;
#endif
register int i;
int j;
char_u *p;
int hiscnt; /* current history line in use */
char_u *lookfor = NULL; /* string to match */
int gotesc = FALSE; /* TRUE when <ESC> just typed */
int do_abbr; /* when TRUE check for abbr. */
int histype; /* history type to be used */
FPOS old_cursor;
colnr_t old_curswant;
int did_incsearch = FALSE;
int incsearch_postponed = FALSE;
int save_msg_scroll = msg_scroll;
int some_key_typed = FALSE; /* one of the keys was typed */
#ifdef USE_MOUSE
/* mouse drag and release events are ignored, unless they are
* preceded with a mouse down event */
int ignore_drag_release = TRUE;
#endif
overstrike = FALSE; /* always start in insert mode */
old_cursor = curwin->w_cursor; /* needs to be restored later */
old_curswant = curwin->w_curswant;
/*
* set some variables for redrawcmd()
*/
cmdfirstc = firstc;
alloc_cmdbuff(0); /* allocate initial cmdbuff */
if (cmdbuff == NULL)
return NULL; /* out of memory */
cmdlen = cmdpos = 0;
cmdspos = 1;
State = CMDLINE;
#ifdef USE_MOUSE
setmouse();
#endif
gotocmdline(TRUE);
msg_outchar(firstc);
/*
* Avoid scrolling when called by a recursive do_cmdline(), e.g. when doing
* ":@0" when register 0 doesn't contain a CR.
*/
msg_scroll = FALSE;
init_history();
hiscnt = hislen; /* set hiscnt to impossible history value */
histype = (firstc == ':' ? 0 : 1);
#ifdef DIGRAPHS
do_digraph(-1); /* init digraph typahead */
#endif
/* collect the command string, handling editing keys */
for (;;)
{
cursorcmd(); /* set the cursor on the right spot */
c = vgetc();
if (KeyTyped)
{
some_key_typed = TRUE;
#ifdef RIGHTLEFT
if (cmd_hkmap)
c = hkmap(c);
#endif
}
if (c == Ctrl('C'))
got_int = FALSE; /* ignore got_int when CTRL-C was typed here */
/* free old command line when finished moving around in the
* history list */
if (lookfor && c != K_S_DOWN && c != K_S_UP &&
c != K_DOWN && c != K_UP &&
c != K_PAGEDOWN && c != K_PAGEUP &&
(cmd_numfiles > 0 || (c != Ctrl('P') && c != Ctrl('N'))))
{
vim_free(lookfor);
lookfor = NULL;
}
/*
* <S-Tab> works like CTRL-P (unless 'wc' is <S-Tab>).
*/
if (c != p_wc && c == K_S_TAB)
c = Ctrl('P');
/* free expanded names when finished walking through matches */
if (cmd_numfiles != -1 && !(c == p_wc && KeyTyped) && c != Ctrl('N') &&
c != Ctrl('P') && c != Ctrl('A') && c != Ctrl('L'))
(void)ExpandOne(NULL, NULL, 0, WILD_FREE);
#ifdef DIGRAPHS
c = do_digraph(c);
#endif
if (c == '\n' || c == '\r' || (c == ESC && (!KeyTyped ||
vim_strchr(p_cpo, CPO_ESC) != NULL)))
{
if (ccheck_abbr(c + ABBR_OFF))
goto cmdline_changed;
outchar('\r'); /* show that we got the return */
screen_cur_col = 0;
flushbuf();
break;
}
/* hitting <ESC> twice means: abandon command line */
/* wildcard expansion is only done when the key is really typed,
* not when it comes from a macro */
if (c == p_wc && !gotesc && KeyTyped)
{
if (cmd_numfiles > 0) /* typed p_wc twice */
i = nextwild(WILD_NEXT);
else /* typed p_wc first time */
i = nextwild(WILD_EXPAND_KEEP);
if (c == ESC)
gotesc = TRUE;
if (i)
goto cmdline_changed;
}
gotesc = FALSE;
if (c == NUL || c == K_ZERO) /* NUL is stored as NL */
c = NL;
do_abbr = TRUE; /* default: check for abbreviation */
switch (c)
{
case K_BS:
case Ctrl('H'):
case K_DEL:
case Ctrl('W'):
/*
* delete current character is the same as backspace on next
* character, except at end of line
*/
if (c == K_DEL && cmdpos != cmdlen)
++cmdpos;
if (cmdpos > 0)
{
j = cmdpos;
if (c == Ctrl('W'))
{
while (cmdpos && vim_isspace(cmdbuff[cmdpos - 1]))
--cmdpos;
i = iswordchar(cmdbuff[cmdpos - 1]);
while (cmdpos && !vim_isspace(cmdbuff[cmdpos - 1]) &&
iswordchar(cmdbuff[cmdpos - 1]) == i)
--cmdpos;
}
else
--cmdpos;
cmdlen -= j - cmdpos;
i = cmdpos;
while (i < cmdlen)
cmdbuff[i++] = cmdbuff[j++];
redrawcmd();
}
else if (cmdlen == 0 && c != Ctrl('W'))
{
vim_free(cmdbuff); /* no commandline to return */
cmdbuff = NULL;
msg_pos(-1, 0);
msg_outchar(' '); /* delete ':' */
redraw_cmdline = TRUE;
goto returncmd; /* back to cmd mode */
}
goto cmdline_changed;
case K_INS:
overstrike = !overstrike;
/* should change shape of cursor */
goto cmdline_not_changed;
/* case '@': only in very old vi */
case Ctrl('U'):
cmdpos = 0;
cmdlen = 0;
cmdspos = 1;
redrawcmd();
goto cmdline_changed;
case ESC: /* get here if p_wc != ESC or when ESC typed twice */
case Ctrl('C'):
gotesc = TRUE; /* will free cmdbuff after putting it in
history */
goto returncmd; /* back to cmd mode */
case Ctrl('R'): /* insert register */
putcmdline('"');
++no_mapping;
c = vgetc();
--no_mapping;
if (c != ESC) /* use ESC to cancel inserting register */
cmdline_paste(c);
redrawcmd();
goto cmdline_changed;
case Ctrl('D'):
{
if (showmatches(cmdbuff) == FAIL)
break; /* Use ^D as normal char instead */
redrawcmd();
continue; /* don't do incremental search now */
}
case K_RIGHT:
case K_S_RIGHT:
do
{
if (cmdpos >= cmdlen)
break;
cmdspos += charsize(cmdbuff[cmdpos]);
++cmdpos;
}
while (c == K_S_RIGHT && cmdbuff[cmdpos] != ' ');
goto cmdline_not_changed;
case K_LEFT:
case K_S_LEFT:
do
{
if (cmdpos <= 0)
break;
--cmdpos;
cmdspos -= charsize(cmdbuff[cmdpos]);
}
while (c == K_S_LEFT && cmdbuff[cmdpos - 1] != ' ');
goto cmdline_not_changed;
#ifdef USE_MOUSE
case K_MIDDLEDRAG:
case K_MIDDLERELEASE:
case K_IGNORE:
goto cmdline_not_changed; /* Ignore mouse */
case K_MIDDLEMOUSE:
# ifdef USE_GUI
/* When GUI is active, also paste when 'mouse' is empty */
if (!gui.in_use)
# endif
if (!mouse_has(MOUSE_COMMAND))
goto cmdline_not_changed; /* Ignore mouse */
# ifdef USE_GUI
if (gui.in_use && yankbuffer == 0)
cmdline_paste('*');
else
# endif
cmdline_paste(yankbuffer);
redrawcmd();
goto cmdline_changed;
case K_LEFTDRAG:
case K_LEFTRELEASE:
case K_RIGHTDRAG:
case K_RIGHTRELEASE:
if (ignore_drag_release)
goto cmdline_not_changed;
/* FALLTHROUGH */
case K_LEFTMOUSE:
case K_RIGHTMOUSE:
if (c == K_LEFTRELEASE || c == K_RIGHTRELEASE)
ignore_drag_release = TRUE;
else
ignore_drag_release = FALSE;
# ifdef USE_GUI
/* When GUI is active, also move when 'mouse' is empty */
if (!gui.in_use)
# endif
if (!mouse_has(MOUSE_COMMAND))
goto cmdline_not_changed; /* Ignore mouse */
cmdspos = 1;
for (cmdpos = 0; cmdpos < cmdlen; ++cmdpos)
{
i = charsize(cmdbuff[cmdpos]);
if (mouse_row <= cmdline_row + cmdspos / Columns &&
mouse_col < cmdspos % Columns + i)
break;
cmdspos += i;
}
goto cmdline_not_changed;
#endif /* USE_MOUSE */
#ifdef USE_GUI
case K_SCROLLBAR:
if (!msg_scrolled)
{
gui_do_scroll();
redrawcmd();
}
goto cmdline_not_changed;
case K_HORIZ_SCROLLBAR:
if (!msg_scrolled)
{
gui_do_horiz_scroll();
redrawcmd();
}
goto cmdline_not_changed;
#endif
case Ctrl('B'): /* begin of command line */
case K_HOME:
cmdpos = 0;
cmdspos = 1;
goto cmdline_not_changed;
case Ctrl('E'): /* end of command line */
case K_END:
cmdpos = cmdlen;
cmdbuff[cmdlen] = NUL;
cmdspos = strsize(cmdbuff) + 1;
goto cmdline_not_changed;
case Ctrl('A'): /* all matches */
if (!nextwild(WILD_ALL))
break;
goto cmdline_changed;
case Ctrl('L'): /* longest common part */
if (!nextwild(WILD_LONGEST))
break;
goto cmdline_changed;
case Ctrl('N'): /* next match */
case Ctrl('P'): /* previous match */
if (cmd_numfiles > 0)
{
if (!nextwild((c == Ctrl('P')) ? WILD_PREV : WILD_NEXT))
break;
goto cmdline_changed;
}
case K_UP:
case K_DOWN:
case K_S_UP:
case K_S_DOWN:
case K_PAGEUP:
case K_PAGEDOWN:
if (hislen == 0) /* no history */
goto cmdline_not_changed;
i = hiscnt;
/* save current command string so it can be restored later */
cmdbuff[cmdpos] = NUL;
if (lookfor == NULL && (lookfor = strsave(cmdbuff)) == NULL)
goto cmdline_not_changed;
j = STRLEN(lookfor);
for (;;)
{
/* one step backwards */
if (c == K_UP || c == K_S_UP || c == Ctrl('P') ||
c == K_PAGEUP)
{
if (hiscnt == hislen) /* first time */
hiscnt = hisidx[histype];
else if (hiscnt == 0 && hisidx[histype] != hislen - 1)
hiscnt = hislen - 1;
else if (hiscnt != hisidx[histype] + 1)
--hiscnt;
else /* at top of list */
{
hiscnt = i;
break;
}
}
else /* one step forwards */
{
/* on last entry, clear the line */
if (hiscnt == hisidx[histype])
{
hiscnt = hislen;
break;
}
/* not on a history line, nothing to do */
if (hiscnt == hislen)
break;
if (hiscnt == hislen - 1) /* wrap around */
hiscnt = 0;
else
++hiscnt;
}
if (hiscnt < 0 || history[histype][hiscnt] == NULL)
{
hiscnt = i;
break;
}
if ((c != K_UP && c != K_DOWN) || hiscnt == i ||
STRNCMP(history[histype][hiscnt],
lookfor, (size_t)j) == 0)
break;
}
if (hiscnt != i) /* jumped to other entry */
{
vim_free(cmdbuff);
if (hiscnt == hislen)
p = lookfor; /* back to the old one */
else
p = history[histype][hiscnt];
alloc_cmdbuff((int)STRLEN(p));
if (cmdbuff == NULL)
goto returncmd;
STRCPY(cmdbuff, p);
cmdpos = cmdlen = STRLEN(cmdbuff);
redrawcmd();
goto cmdline_changed;
}
beep_flush();
goto cmdline_not_changed;
case Ctrl('V'):
case Ctrl('Q'):
#ifdef USE_MOUSE
ignore_drag_release = TRUE;
#endif
putcmdline('^');
c = get_literal(); /* get next (two) character(s) */
do_abbr = FALSE; /* don't do abbreviation now */
break;
#ifdef DIGRAPHS
case Ctrl('K'):
#ifdef USE_MOUSE
ignore_drag_release = TRUE;
#endif
putcmdline('?');
++no_mapping;
++allow_keys;
c = vgetc();
--no_mapping;
--allow_keys;
if (c != ESC) /* ESC cancels CTRL-K */
{
if (IS_SPECIAL(c)) /* insert special key code */
break;
if (charsize(c) == 1)
putcmdline(c);
++no_mapping;
++allow_keys;
cc = vgetc();
--no_mapping;
--allow_keys;
if (cc != ESC) /* ESC cancels CTRL-K */
{
c = getdigraph(c, cc, TRUE);
break;
}
}
redrawcmd();
goto cmdline_not_changed;
#endif /* DIGRAPHS */
#ifdef RIGHTLEFT
case Ctrl('_'): /* CTRL-_: switch language mode */
cmd_hkmap = !cmd_hkmap;
goto cmdline_not_changed;
#endif
default:
/*
* Normal character with no special meaning. Just set mod_mask
* to 0x0 so that typing Shift-Space in the GUI doesn't enter
* the string <S-Space>. This should only happen after ^V.
*/
if (!IS_SPECIAL(c))
mod_mask = 0x0;
break;
}
/* we come here if we have a normal character */
if (do_abbr && (IS_SPECIAL(c) || !iswordchar(c)) && ccheck_abbr(c))
goto cmdline_changed;
/*
* put the character in the command line
*/
if (IS_SPECIAL(c) || mod_mask != 0x0)
put_on_cmdline(get_special_key_name(c, mod_mask), -1, TRUE);
else
{
IObuff[0] = c;
put_on_cmdline(IObuff, 1, TRUE);
}
goto cmdline_changed;
/*
* This part implements incremental searches for "/" and "?"
* Jump to cmdline_not_changed when a character has been read but the command
* line did not change. Then we only search and redraw if something changed in
* the past.
* Jump to cmdline_changed when the command line did change.
* (Sorry for the goto's, I know it is ugly).
*/
cmdline_not_changed:
if (!incsearch_postponed)
continue;
cmdline_changed:
if (p_is && (firstc == '/' || firstc == '?'))
{
/* if there is a character waiting, search and redraw later */
if (char_avail())
{
incsearch_postponed = TRUE;
continue;
}
incsearch_postponed = FALSE;
curwin->w_cursor = old_cursor; /* start at old position */
/* If there is no command line, don't do anything */
if (cmdlen == 0)
i = 0;
else
{
cmdbuff[cmdlen] = NUL;
emsg_off = TRUE; /* So it doesn't beep if bad expr */
i = do_search(firstc, cmdbuff, count,
SEARCH_KEEP + SEARCH_OPT + SEARCH_NOOF);
emsg_off = FALSE;
}
if (i)
{
highlight_match = TRUE; /* highlight position */
cursupdate();
}
else
{
highlight_match = FALSE; /* don't highlight */
/* vim_beep(); */ /* even beeps when invalid expr, e.g. "[" */
}
updateScreen(NOT_VALID);
redrawcmdline();
did_incsearch = TRUE;
}
}
returncmd:
if (did_incsearch)
{
curwin->w_cursor = old_cursor;
curwin->w_curswant = old_curswant;
highlight_match = FALSE;
redraw_later(NOT_VALID);
}
if (cmdbuff != NULL)
{
/*
* Put line in history buffer (":" only when it was typed).
*/
cmdbuff[cmdlen] = NUL;
if (cmdlen != 0 && (some_key_typed || firstc != ':'))
{
add_to_history(histype, cmdbuff);
if (firstc == ':')
{
vim_free(new_last_cmdline);
new_last_cmdline = strsave(cmdbuff);
}
}
if (gotesc) /* abandon command line */
{
vim_free(cmdbuff);
cmdbuff = NULL;
MSG("");
redraw_cmdline = TRUE;
}
}
/*
* If the screen was shifted up, redraw the whole screen (later).
* If the line is too long, clear it, so ruler and shown command do
* not get printed in the middle of it.
*/
msg_check();
msg_scroll = save_msg_scroll;
State = NORMAL;
#ifdef USE_MOUSE
setmouse();
#endif
return cmdbuff;
}
/*
* Put the given string, of the given length, onto the command line.
* If len is -1, then STRLEN() is used to calculate the length.
* If 'redraw' is TRUE then the new part of the command line, and the remaining
* part will be redrawn, otherwise it will not. If this function is called
* twice in a row, then 'redraw' should be FALSE and redrawcmd() should be
* called afterwards.
*/
int
put_on_cmdline(str, len, redraw)
char_u *str;
int len;
int redraw;
{
int i;
if (len < 0)
len = STRLEN(str);
/* Check if cmdbuff needs to be longer */
if (cmdlen + len + 1 >= cmdbufflen)
i = realloc_cmdbuff(cmdlen + len);
else
i = OK;
if (i == OK)
{
if (!overstrike)
{
vim_memmove(cmdbuff + cmdpos + len, cmdbuff + cmdpos,
(size_t)(cmdlen - cmdpos));
cmdlen += len;
}
else if (cmdpos + len > cmdlen)
cmdlen = cmdpos + len;
vim_memmove(cmdbuff + cmdpos, str, (size_t)len);
if (redraw)
msg_outtrans_len(cmdbuff + cmdpos, cmdlen - cmdpos);
cmdpos += len;
while (len--)
cmdspos += charsize(str[len]);
}
if (redraw)
msg_check();
return i;
}
void
alloc_cmdbuff(len)
int len;
{
/*
* give some extra space to avoid having to allocate all the time
*/
if (len < 80)
len = 100;
else
len += 20;
cmdbuff = alloc(len); /* caller should check for out of memory */
cmdbufflen = len;
}
/*
* Re-allocate the command line to length len + something extra.
* return FAIL for failure, OK otherwise
*/
int
realloc_cmdbuff(len)
int len;
{
char_u *p;
p = cmdbuff;
alloc_cmdbuff(len); /* will get some more */
if (cmdbuff == NULL) /* out of memory */
{
cmdbuff = p; /* keep the old one */
return FAIL;
}
vim_memmove(cmdbuff, p, (size_t)cmdlen);
vim_free(p);
return OK;
}
/*
* put a character on the command line.
* Used for CTRL-V and CTRL-K
*/
static void
putcmdline(c)
int c;
{
char_u buf[1];
buf[0] = c;
msg_outtrans_len(buf, 1);
msg_outtrans_len(cmdbuff + cmdpos, cmdlen - cmdpos);
cursorcmd();
}
/*
* this fuction is called when the screen size changes and with incremental
* search
*/
void
redrawcmdline()
{
msg_scrolled = 0;
need_wait_return = FALSE;
compute_cmdrow();
redrawcmd();
cursorcmd();
}
void
compute_cmdrow()
{
cmdline_row = lastwin->w_winpos + lastwin->w_height +
lastwin->w_status_height;
}
/*
* Redraw what is currently on the command line.
*/
static void
redrawcmd()
{
register int i;
msg_start();
msg_outchar(cmdfirstc);
msg_outtrans_len(cmdbuff, cmdlen);
msg_clr_eos();
cmdspos = 1;
for (i = 0; i < cmdlen && i < cmdpos; ++i)
cmdspos += charsize(cmdbuff[i]);
/*
* An emsg() before may have set msg_scroll and need_sleep. These are used
* in normal mode, in cmdline mode we can reset them now.
*/
msg_scroll = FALSE; /* next message overwrites cmdline */
#ifdef SLEEP_IN_EMSG
need_sleep = FALSE; /* don't sleep */
#endif
}
static void
cursorcmd()
{
msg_pos(cmdline_row + (cmdspos / (int)Columns), cmdspos % (int)Columns);
windgoto(msg_row, msg_col);
}
/*
* Check the word in front of the cursor for an abbreviation.
* Called when the non-id character "c" has been entered.
* When an abbreviation is recognized it is removed from the text with
* backspaces and the replacement string is inserted, followed by "c".
*/
static int
ccheck_abbr(c)
int c;
{
if (p_paste || no_abbr) /* no abbreviations or in paste mode */
return FALSE;
return check_abbr(c, cmdbuff, cmdpos, 0);
}
/*
* do_cmdline(): execute an Ex command line
*
* 1. If no line given, get one.
* 2. Split up in parts separated with '|'.
*
* This function may be called recursively!
*
* If 'sourcing' is TRUE, the command will be included in the error message.
* If 'repeating' is TRUE, there is no wait_return() and friends.
*
* return FAIL if commandline could not be executed, OK otherwise
*/
int
do_cmdline(cmdline, sourcing, repeating)
char_u *cmdline;
int sourcing;
int repeating;
{
int cmdlinelen;
char_u *nextcomm;
static int recursive = 0; /* recursive depth */
int got_cmdline = FALSE; /* TRUE when cmdline was typed */
int msg_didout_before_start;
/*
* 1. If no line given: Get a line in cmdbuff.
* If a line is given: Copy it into cmdbuff.
* After this we don't use cmdbuff but cmdline, because of recursiveness
*/
if (cmdline == NULL)
{
if ((cmdline = getcmdline(':', 1L)) == NULL)
{
/* don't call wait_return for aborted command line */
need_wait_return = FALSE;
return FAIL;
}
got_cmdline = TRUE;
}
else
{
/* Make a copy of the command so we can mess with it. */
alloc_cmdbuff((int)STRLEN(cmdline));
if (cmdbuff == NULL)
return FAIL;
STRCPY(cmdbuff, cmdline);
cmdline = cmdbuff;
}
cmdlinelen = cmdbufflen; /* we need to copy it for recursiveness */
/*
* All output from the commands is put below each other, without waiting for a
* return. Don't do this when executing commands from a script or when being
* called recursive (e.g. for ":e +command file").
*/
msg_didout_before_start = msg_didout;
if (!repeating && !recursive)
{
msg_didany = FALSE; /* no output yet */
msg_start();
msg_scroll = TRUE; /* put messages below each other */
#ifdef SLEEP_IN_EMSG
++dont_sleep; /* don't sleep in emsg() */
#endif
++no_wait_return; /* dont wait for return until finished */
++RedrawingDisabled;
}
/*
* 2. Loop for each '|' separated command.
* do_one_cmd will set nextcomm to NULL if there is no trailing '|'.
* cmdline and cmdlinelen may change, e.g. for '%' and '#' expansion.
*/
++recursive;
for (;;)
{
nextcomm = do_one_cmd(&cmdline, &cmdlinelen, sourcing);
if (nextcomm == NULL)
break;
STRCPY(cmdline, nextcomm);
}
--recursive;
vim_free(cmdline);
/*
* If there was too much output to fit on the command line, ask the user to
* hit return before redrawing the screen. With the ":global" command we do
* this only once after the command is finished.
*/
if (!repeating && !recursive)
{
--RedrawingDisabled;
#ifdef SLEEP_IN_EMSG
--dont_sleep;
#endif
--no_wait_return;
msg_scroll = FALSE;
if (need_wait_return || (msg_check() && !dont_wait_return))
{
/*
* The msg_start() above clears msg_didout. The wait_return we do
* here should not overwrite the command that may be shown before
* doing that.
*/
msg_didout = msg_didout_before_start;
wait_return(FALSE);
}
}
/*
* If the command was typed, remember it for register :
* Do this AFTER executing the command to make :@: work.
*/
if (got_cmdline && new_last_cmdline != NULL)
{
vim_free(last_cmdline);
last_cmdline = new_last_cmdline;
new_last_cmdline = NULL;
}
return OK;
}
static char *(make_cmd_chars[6]) =
{ " \164\145a",
"\207\171\204\170\060\175\171\174\173\117\032",
" c\157\146\146e\145",
"\200\174\165\161\203\165\060\171\176\203\165\202\204\060\163\177\171\176\060\204\177\060\202\205\176\060\175\161\173\165\032",
" \164o\141\163t",
"\136\137\122\137\124\151\060\165\210\200\165\163\204\203\060\204\170\165\060\143\200\161\176\171\203\170\060\171\176\201\205\171\203\171\204\171\177\176\061\032"
};
/*
* Execute one Ex command.
*
* If 'sourcing' is TRUE, the command will be included in the error message.
*
* 2. skip comment lines and leading space
* 3. parse range
* 4. parse command
* 5. parse arguments
* 6. switch on command name
*
* This function may be called recursively!
*/
static char_u *
do_one_cmd(cmdlinep, cmdlinelenp, sourcing)
char_u **cmdlinep;
int *cmdlinelenp;
int sourcing;
{
char_u *p;
char_u *q;
char_u *s;
char_u *cmd, *arg;
char_u *do_ecmd_cmd = NULL; /* +command for do_ecmd() */
linenr_t do_ecmd_lnum = 0; /* lnum file for do_ecmd() */
int i = 0; /* init to shut up gcc */
int len;
int cmdidx;
long argt;
register linenr_t lnum;
long n = 0; /* init to shut up gcc */
int addr_count; /* number of address specs */
FPOS pos;
int append = FALSE; /* write with append */
int usefilter = FALSE; /* no read/write but filter */
char_u *nextcomm = NULL; /* no next command yet */
int amount = 0; /* for ":>"; init for gcc */
char_u *errormsg = NULL; /* error message */
WIN *old_curwin = NULL; /* init for GCC */
/* when not editing the last file :q has to be typed twice */
if (quitmore)
--quitmore;
did_emsg = FALSE; /* will be set to TRUE when emsg() used, in which
* case we set nextcomm to NULL to cancel the
* whole command line */
/*
* 2. skip comment lines and leading space and colons
*/
for (cmd = *cmdlinep; vim_strchr((char_u *)" \t:", *cmd) != NULL; cmd++)
;
if (*cmd == '"' || *cmd == NUL) /* ignore comment and empty lines */
goto doend;
/*
* 3. parse a range specifier of the form: addr [,addr] [;addr] ..
*
* where 'addr' is:
*
* % (entire file)
* $ [+-NUM]
* 'x [+-NUM] (where x denotes a currently defined mark)
* . [+-NUM]
* [+-NUM]..
* NUM
*
* The cmd pointer is updated to point to the first character following the
* range spec. If an initial address is found, but no second, the upper bound
* is equal to the lower.
*/
addr_count = 0;
--cmd;
do
{
line1 = line2;
line2 = curwin->w_cursor.lnum; /* default is current line number */
cmd = skipwhite(cmd + 1); /* skip ',' or ';' and following ' ' */
lnum = get_address(&cmd);
if (cmd == NULL) /* error detected */
goto doend;
if (lnum == MAXLNUM)
{
if (*cmd == '%') /* '%' - all lines */
{
++cmd;
line1 = 1;
line2 = curbuf->b_ml.ml_line_count;
++addr_count;
}
else if (*cmd == '*') /* '*' - visual area */
{
FPOS *fp;
++cmd;
fp = getmark('<', FALSE);
if (check_mark(fp) == FAIL)
goto doend;
line1 = fp->lnum;
fp = getmark('>', FALSE);
if (check_mark(fp) == FAIL)
goto doend;
line2 = fp->lnum;
++addr_count;
}
}
else
line2 = lnum;
addr_count++;
if (*cmd == ';')
{
if (line2 == 0)
curwin->w_cursor.lnum = 1;
else
curwin->w_cursor.lnum = line2;
}
} while (*cmd == ',' || *cmd == ';');
/* One address given: set start and end lines */
if (addr_count == 1)
{
line1 = line2;
/* ... but only implicit: really no address given */
if (lnum == MAXLNUM)
addr_count = 0;
}
/*
* 4. parse command
*/
/*
* Skip ':' and any white space
*/
cmd = skipwhite(cmd);
if (*cmd == ':')
cmd = skipwhite(cmd + 1);
/*
* If we got a line, but no command, then go to the line.
* If we find a '|' or '\n' we set nextcomm.
*/
if (*cmd == NUL || *cmd == '"' ||
((*cmd == '|' || *cmd == '\n') &&
(nextcomm = cmd + 1) != NULL)) /* just an assignment */
{
/*
* strange vi behaviour:
* ":3" jumps to line 3
* ":3|..." prints line 3
* ":|" prints current line
*/
if (*cmd == '|')
{
cmdidx = CMD_print;
goto cmdswitch; /* UGLY goto */
}
if (addr_count != 0)
{
if (line2 == 0)
curwin->w_cursor.lnum = 1;
else if (line2 > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
else
curwin->w_cursor.lnum = line2;
beginline(MAYBE);
/* This causes problems for ":234", since displaying is disabled
* here */
/* cursupdate(); */
}
goto doend;
}
/*
* Isolate the command and search for it in the command table.
* Exeptions:
* - the 'k' command can directly be followed by any character.
* - the 's' command can be followed directly by 'c', 'g' or 'r'
* but :sre[wind] is another command.
*/
if (*cmd == 'k')
{
cmdidx = CMD_k;
p = cmd + 1;
}
else if (*cmd == 's' && vim_strchr((char_u *)"cgr", cmd[1]) != NULL &&
STRNCMP("sre", cmd, (size_t)3) != 0)
{
cmdidx = CMD_substitute;
p = cmd + 1;
}
else
{
p = cmd;
while (isalpha(*p))
++p;
/* check for non-alpha command */
if (p == cmd && vim_strchr((char_u *)"@!=><&~#", *p) != NULL)
++p;
i = (int)(p - cmd);
for (cmdidx = 0; cmdidx < CMD_SIZE; ++cmdidx)
if (STRNCMP(cmdnames[cmdidx].cmd_name, (char *)cmd, (size_t)i) == 0)
break;
if (i == 0 || cmdidx == CMD_SIZE)
{
STRCPY(IObuff, "Not an editor command");
if (!sourcing)
{
STRCAT(IObuff, ": ");
STRNCAT(IObuff, *cmdlinep, 40);
}
errormsg = IObuff;
goto doend;
}
}
if (*p == '!') /* forced commands */
{
++p;
forceit = TRUE;
}
else
forceit = FALSE;
/*
* 5. parse arguments
*/
argt = cmdnames[cmdidx].cmd_argt;
if (!(argt & RANGE) && addr_count) /* no range allowed */
{
errormsg = e_norange;
goto doend;
}
if (!(argt & BANG) && forceit) /* no <!> allowed */
{
errormsg = e_nobang;
goto doend;
}
/*
* If the range is backwards, ask for confirmation and, if given, swap
* line1 & line2 so it's forwards again.
* When global command is busy, don't ask, will fail below.
*/
if (!global_busy && line1 > line2)
{
if (sourcing)
{
errormsg = (char_u *)"Backwards range given";
goto doend;
}
else if (ask_yesno((char_u *)"Backwards range given, OK to swap", FALSE) != 'y')
goto doend;
lnum = line1;
line1 = line2;
line2 = lnum;
}
/*
* don't complain about the range if it is not used
* (could happen if line_count is accidently set to 0)
*/
if (line1 < 0 || line2 < 0 || line1 > line2 || ((argt & RANGE) &&
!(argt & NOTADR) && line2 > curbuf->b_ml.ml_line_count))
{
errormsg = e_invrange;
goto doend;
}
if ((argt & NOTADR) && addr_count == 0) /* default is 1, not cursor */
line2 = 1;
if (!(argt & ZEROR)) /* zero in range not allowed */
{
if (line1 == 0)
line1 = 1;
if (line2 == 0)
line2 = 1;
}
/*
* for the :make command we insert the 'makeprg' option here,
* so things like % get expanded
*/
if (cmdidx == CMD_make)
{
alloc_cmdbuff((int)(STRLEN(p_mp) + STRLEN(p) + 2));
if (cmdbuff == NULL) /* out of memory */
goto doend;
/*
* Check for special command characters and echo them.
*/
for (i = 0; i < 6; i += 2)
if (!STRCMP(make_cmd_chars[i], p))
for (s = (char_u *)(make_cmd_chars[i + 1]); *s; ++s)
msg_outchar(*s - 16);
STRCPY(cmdbuff, p_mp);
STRCAT(cmdbuff, " ");
STRCAT(cmdbuff, p);
/* 'cmd' is not set here, because it is not used at CMD_make */
vim_free(*cmdlinep);
*cmdlinep = cmdbuff;
*cmdlinelenp = cmdbufflen;
p = cmdbuff;
}
/*
* Skip to start of argument.
* Don't do this for the ":!" command, because ":!! -l" needs the space.
*/
if (cmdidx == CMD_bang)
arg = p;
else
arg = skipwhite(p);
if (cmdidx == CMD_write)
{
if (*arg == '>') /* append */
{
if (*++arg != '>') /* typed wrong */
{
errormsg = (char_u *)"Use w or w>>";
goto doend;
}
arg = skipwhite(arg + 1);
append = TRUE;
}
else if (*arg == '!') /* :w !filter */
{
++arg;
usefilter = TRUE;
}
}
if (cmdidx == CMD_read)
{
if (forceit)
{
usefilter = TRUE; /* :r! filter if forceit */
forceit = FALSE;
}
else if (*arg == '!') /* :r !filter */
{
++arg;
usefilter = TRUE;
}
}
if (cmdidx == CMD_lshift || cmdidx == CMD_rshift)
{
amount = 1;
while (*arg == *cmd) /* count number of '>' or '<' */
{
++arg;
++amount;
}
arg = skipwhite(arg);
}
/*
* Check for "+command" argument, before checking for next command.
* Don't do this for ":read !cmd" and ":write !cmd".
*/
if ((argt & EDITCMD) && !usefilter)
do_ecmd_cmd = getargcmd(&arg);
/*
* Check for '|' to separate commands and '"' to start comments.
* Don't do this for ":read !cmd" and ":write !cmd".
*/
if ((argt & TRLBAR) && !usefilter)
{
for (p = arg; *p; ++p)
{
if (*p == Ctrl('V'))
{
if (argt & (USECTRLV | XFILE))
++p; /* skip CTRL-V and next char */
else
STRCPY(p, p + 1); /* remove CTRL-V and skip next char */
if (*p == NUL) /* stop at NUL after CTRL-V */
break;
}
else if ((*p == '"' && !(argt & NOTRLCOM)) ||
*p == '|' || *p == '\n')
{
/*
* We remove the '\' before the '|', unless USECTRLV is used
* AND 'b' is present in 'cpoptions'.
*/
if ((vim_strchr(p_cpo, CPO_BAR) == NULL ||
!(argt & USECTRLV)) && *(p - 1) == '\\')
{
STRCPY(p - 1, p); /* remove the backslash */
--p;
}
else
{
if (*p == '|' || *p == '\n')
nextcomm = p + 1;
*p = NUL;
break;
}
}
}
if (!(argt & NOTRLCOM)) /* remove trailing spaces */
del_trailing_spaces(arg);
}
/*
* Check for <newline> to end a shell command.
* Also do this for ":read !cmd" and ":write !cmd".
*/
else if (cmdidx == CMD_bang || usefilter)
{
for (p = arg; *p; ++p)
{
if (*p == '\\' && p[1])
++p;
else if (*p == '\n')
{
nextcomm = p + 1;
*p = NUL;
break;
}
}
}
if ((argt & DFLALL) && addr_count == 0)
{
line1 = 1;
line2 = curbuf->b_ml.ml_line_count;
}
regname = 0;
/* accept numbered register only when no count allowed (:put) */
if ((argt & REGSTR) && *arg != NUL && is_yank_buffer(*arg, FALSE) &&
!((argt & COUNT) && isdigit(*arg)))
{
regname = *arg;
arg = skipwhite(arg + 1);
}
if ((argt & COUNT) && isdigit(*arg))
{
n = getdigits(&arg);
arg = skipwhite(arg);
if (n <= 0)
{
errormsg = e_zerocount;
goto doend;
}
if (argt & NOTADR) /* e.g. :buffer 2, :sleep 3 */
{
line2 = n;
if (addr_count == 0)
addr_count = 1;
}
else
{
line1 = line2;
line2 += n - 1;
++addr_count;
/*
* Be vi compatible: no error message for out of range.
*/
if (line2 > curbuf->b_ml.ml_line_count)
line2 = curbuf->b_ml.ml_line_count;
}
}
/* no arguments allowed */
if (!(argt & EXTRA) && *arg != NUL &&
vim_strchr((char_u *)"|\"", *arg) == NULL)
{
errormsg = e_trailing;
goto doend;
}
if ((argt & NEEDARG) && *arg == NUL)
{
errormsg = e_argreq;
goto doend;
}
/*
* change '%' to curbuf->b_filename
* '#' to curwin->w_altfile
* '<cword>' to word under the cursor
* '<cWORD>' to WORD under the cursor
* '<cfile>' to path name under the cursor
* '<afile>' to file name for autocommand
*/
if (argt & XFILE)
{
char_u *buf = NULL;
int expand_wildcards; /* need to expand wildcards */
int spec_idx;
static char *(spec_str[]) =
{
"%",
#define SPEC_PERC 0
"#",
#define SPEC_HASH 1
"<cword>", /* cursor word */
#define SPEC_CWORD 2
"<cWORD>", /* cursor WORD */
#define SPEC_CCWORD 3
"<cfile>", /* cursor path name */
#define SPEC_CFILE 4
"<afile>" /* autocommand file name */
#define SPEC_AFILE 5
};
#define SPEC_COUNT 6
/*
* Decide to expand wildcards *before* replacing '%', '#', etc. If
* the file name contains a wildcard it should not cause expanding.
* (it will be expanded anyway if there is a wildcard before replacing).
*/
expand_wildcards = mch_has_wildcard(arg);
for (p = arg; *p; ++p)
{
/*
* Check if there is something to do.
*/
for (spec_idx = 0; spec_idx < SPEC_COUNT; ++spec_idx)
{
n = strlen(spec_str[spec_idx]);
if (STRNCMP(p, spec_str[spec_idx], n) == 0)
break;
}
if (spec_idx == SPEC_COUNT) /* no match */
continue;
/*
* Skip when preceded with a backslash "\%" and "\#".
* Note: In "\\%" the % is also not recognized!
*/
if (*(p - 1) == '\\')
{
--p;
STRCPY(p, p + 1); /* remove escaped char */
continue;
}
/*
* word or WORD under cursor
*/
if (spec_idx == SPEC_CWORD || spec_idx == SPEC_CCWORD)
{
len = find_ident_under_cursor(&q, spec_idx == SPEC_CWORD ?
(FIND_IDENT|FIND_STRING) : FIND_STRING);
if (len == 0)
goto doend;
}
/*
* '#': Alternate file name
* '%': Current file name
* File name under the cursor
* File name for autocommand
* and following modifiers
*/
else
{
switch (spec_idx)
{
case SPEC_PERC: /* '%': current file */
if (curbuf->b_filename == NULL)
{
errormsg = (char_u *)"No file name to substitute for '%'";
goto doend;
}
q = curbuf->b_xfilename;
break;
case SPEC_HASH: /* '#' or "#99": alternate file */
q = p + 1;
i = (int)getdigits(&q);
n = q - p; /* length of what we expand */
if (buflist_name_nr(i, &q, &do_ecmd_lnum) ==
FAIL)
{
errormsg = (char_u *)"no alternate filename to substitute for '#'";
goto doend;
}
break;
case SPEC_CFILE: /* file name under cursor */
q = file_name_at_cursor(FNAME_MESS|FNAME_HYP);
if (q == NULL)
goto doend;
buf = q;
break;
case SPEC_AFILE: /* file name for autocommand */
q = autocmd_fname;
if (q == NULL)
{
errormsg = (char_u *)"no autocommand filename to substitute for \"<afile>\"";
goto doend;
}
break;
}
len = STRLEN(q); /* length of new string */
if (p[n] == '<') /* remove the file name extension */
{
++n;
if ((s = vim_strrchr(q, '.')) != NULL && s >= gettail(q))
len = s - q;
}
else
{
char_u *tail;
/* ":p" - full path/filename */
if (p[n] == ':' && p[n + 1] == 'p')
{
n += 2;
s = FullName_save(q);
vim_free(buf); /* free any allocated file name */
if (s == NULL)
goto doend;
q = s;
len = STRLEN(q);
buf = q;
}
tail = gettail(q);
/* ":h" - head, remove "/filename" */
/* ":h" can be repeated */
while (p[n] == ':' && p[n + 1] == 'h')
{
n += 2;
while (tail > q && ispathsep(tail[-1]))
--tail;
len = tail - q;
while (tail > q && !ispathsep(tail[-1]))
--tail;
}
/* ":t" - tail, just the basename */
if (p[n] == ':' && p[n + 1] == 't')
{
n += 2;
len -= tail - q;
q = tail;
}
/* ":e" - extension */
/* ":e" can be repeated */
/* ":r" - root, without extension */
/* ":r" can be repeated */
while (p[n] == ':' &&
(p[n + 1] == 'e' || p[n + 1] == 'r'))
{
/* find a '.' in the tail:
* - for second :e: before the current fname
* - otherwise: The last '.'
*/
if (p[n + 1] == 'e' && q > tail)
s = q - 2;
else
s = q + len - 1;
for ( ; s > tail; --s)
if (s[0] == '.')
break;
if (p[n + 1] == 'e') /* :e */
{
if (s > tail)
{
len += q - (s + 1);
q = s + 1;
}
else if (q <= tail)
len = 0;
}
else /* :r */
{
if (s > tail) /* remove one extension */
len = s - q;
}
n += 2;
}
}
/* TODO - ":s/pat/foo/" - substitute */
/* if (p[n] == ':' && p[n + 1] == 's') */
}
/*
* The new command line is build in cmdbuff[].
* First allocate it.
*/
i = STRLEN(*cmdlinep) + len + 3;
if (nextcomm)
i += STRLEN(nextcomm); /* add space for next command */
alloc_cmdbuff(i);
if (cmdbuff == NULL) /* out of memory! */
goto doend;
i = p - *cmdlinep; /* length of part before c */
vim_memmove(cmdbuff, *cmdlinep, (size_t)i);
vim_memmove(cmdbuff + i, q, (size_t)len); /* append the string */
i += len; /* remember the end of the string */
STRCPY(cmdbuff + i, p + n); /* append what is after '#' or '%' */
p = cmdbuff + i - 1; /* remember where to continue */
vim_free(buf); /* free any allocated string */
if (nextcomm) /* append next command */
{
i = STRLEN(cmdbuff) + 1;
STRCPY(cmdbuff + i, nextcomm);
nextcomm = cmdbuff + i;
}
cmd = cmdbuff + (cmd - *cmdlinep);
arg = cmdbuff + (arg - *cmdlinep);
vim_free(*cmdlinep);
*cmdlinep = cmdbuff;
*cmdlinelenp = cmdbufflen;
}
/*
* One file argument: expand wildcards.
* Don't do this with ":r !command" or ":w !command".
*/
if ((argt & NOSPC) && !usefilter)
{
#if defined(UNIX)
/*
* Only for Unix we check for more than one file name.
* For other systems spaces are considered to be part
* of the file name.
* Only check here if there is no wildcard, otherwise ExpandOne
* will check for errors. This allows ":e `ls ve*.c`" on Unix.
*/
if (!expand_wildcards)
for (p = arg; *p; ++p)
{
/* skip escaped characters */
if (p[1] && (*p == '\\' || *p == Ctrl('V')))
++p;
else if (vim_iswhite(*p))
{
errormsg = (char_u *)"Only one file name allowed";
goto doend;
}
}
#endif
/*
* halve the number of backslashes (this is vi compatible)
*/
backslash_halve(arg, expand_wildcards);
if (expand_wildcards)
{
if ((p = ExpandOne(arg, NULL, WILD_LIST_NOTFOUND,
WILD_EXPAND_FREE)) == NULL)
goto doend;
n = arg - *cmdlinep;
i = STRLEN(p) + n;
if (nextcomm)
i += STRLEN(nextcomm);
alloc_cmdbuff(i);
if (cmdbuff != NULL)
{
STRNCPY(cmdbuff, *cmdlinep, n);
STRCPY(cmdbuff + n, p);
if (nextcomm) /* append next command */
{
i = STRLEN(cmdbuff) + 1;
STRCPY(cmdbuff + i, nextcomm);
nextcomm = cmdbuff + i;
}
cmd = cmdbuff + (cmd - *cmdlinep);
arg = cmdbuff + n;
vim_free(*cmdlinep);
*cmdlinep = cmdbuff;
*cmdlinelenp = cmdbufflen;
}
vim_free(p);
}
}
}
/*
* Accept buffer name. Cannot be used at the same time with a buffer
* number.
*/
if ((argt & BUFNAME) && *arg && addr_count == 0)
{
/*
* :bdelete and :bunload take several arguments, separated by spaces:
* find next space (skipping over escaped characters).
* The others take one argument: ignore trailing spaces.
*/
if (cmdidx == CMD_bdelete || cmdidx == CMD_bunload)
p = skiptowhite_esc(arg);
else
{
p = arg + STRLEN(arg);
while (p > arg && vim_iswhite(p[-1]))
--p;
}
line2 = buflist_findpat(arg, p);
if (line2 < 0) /* failed */
goto doend;
addr_count = 1;
arg = skipwhite(p);
}
/*
* 6. switch on command name
* arg points to the argument of the command
* nextcomm points to the next command (if any)
* cmd points to the name of the command (except for :make)
* cmdidx is the index for the command
* forceit is TRUE if ! present
* addr_count is the number of addresses given
* line1 is the first line number
* line2 is the second line number or count
* do_ecmd_cmd is +command argument to be used in edited file
* do_ecmd_lnum is the line number in edited file
* append is TRUE with ":w >>file" command
* usefilter is TRUE with ":w !command" and ":r!command"
* amount is number of '>' or '<' for shift command
*/
cmdswitch:
switch (cmdidx)
{
/*
* quit current window, quit Vim if closed the last window
*/
case CMD_quit:
/* if more files or windows we won't exit */
if (check_more(FALSE) == OK && only_one_window())
exiting = TRUE;
if (check_changed(curbuf, FALSE, FALSE) ||
check_more(TRUE) == FAIL ||
(only_one_window() && check_changed_any()))
{
exiting = FALSE;
settmode(1);
break;
}
if (only_one_window()) /* quit last window */
getout(0);
close_window(curwin, TRUE); /* may free buffer */
break;
/*
* try to quit all windows
*/
case CMD_qall:
exiting = TRUE;
if (!check_changed_any())
getout(0);
exiting = FALSE;
settmode(1);
break;
/*
* close current window, unless it is the last one
*/
case CMD_close:
close_window(curwin, FALSE); /* don't free buffer */
break;
/*
* close all but current window, unless it is the last one
*/
case CMD_only:
close_others(TRUE);
break;
case CMD_stop:
case CMD_suspend:
#ifdef WIN32
/*
* Check if external commands are allowed now.
*/
if (can_end_termcap_mode(TRUE) == FALSE)
break;
#endif
if (!forceit)
autowrite_all();
windgoto((int)Rows - 1, 0);
outchar('\n');
flushbuf();
stoptermcap();
mch_restore_title(3); /* restore window titles */
mch_suspend(); /* call machine specific function */
maketitle();
starttermcap();
scroll_start(); /* scroll screen before redrawing */
must_redraw = CLEAR;
set_winsize(0, 0, FALSE); /* May have resized window -- webb */
break;
case CMD_exit:
case CMD_xit:
case CMD_wq:
/* if more files or windows we won't exit */
if (check_more(FALSE) == OK && only_one_window())
exiting = TRUE;
if (((cmdidx == CMD_wq || curbuf->b_changed) &&
do_write(arg, FALSE) == FAIL) ||
check_more(TRUE) == FAIL ||
(only_one_window() && check_changed_any()))
{
exiting = FALSE;
settmode(1);
break;
}
if (only_one_window()) /* quit last window, exit Vim */
getout(0);
close_window(curwin, TRUE); /* quit current window, may free buffer */
break;
case CMD_xall: /* write all changed files and exit */
case CMD_wqall: /* write all changed files and quit */
exiting = TRUE;
/* FALLTHROUGH */
case CMD_wall: /* write all changed files */
{
BUF *buf;
int error = 0;
for (buf = firstbuf; buf != NULL; buf = buf->b_next)
{
if (buf->b_changed)
{
if (buf->b_filename == NULL)
{
emsg(e_noname);
++error;
}
else if (!forceit && buf->b_p_ro)
{
EMSG2("\"%s\" is readonly, use ! to write anyway", buf->b_xfilename);
++error;
}
else if (buf_write_all(buf) == FAIL)
++error;
}
}
if (exiting)
{
if (!error)
getout(0); /* exit Vim */
exiting = FALSE;
settmode(1);
}
}
break;
case CMD_preserve: /* put everything in .swp file */
ml_preserve(curbuf, TRUE);
break;
case CMD_recover: /* recover file */
recoverymode = TRUE;
if (!check_changed(curbuf, FALSE, TRUE) &&
(*arg == NUL || setfname(arg, NULL, TRUE) == OK))
ml_recover();
recoverymode = FALSE;
break;
case CMD_args:
/*
* ":args file": handle like :next
*/
if (*arg != NUL && *arg != '|' && *arg != '\n')
goto do_next;
if (arg_count == 0) /* no file name list */
{
if (check_fname() == OK) /* check for no file name */
smsg((char_u *)"[%s]", curbuf->b_filename);
break;
}
/*
* Overwrite the command, in most cases there is no scrolling
* required and no wait_return().
*/
gotocmdline(TRUE);
for (i = 0; i < arg_count; ++i)
{
if (i == curwin->w_arg_idx)
msg_outchar('[');
msg_outtrans(arg_files[i]);
if (i == curwin->w_arg_idx)
msg_outchar(']');
msg_outchar(' ');
}
break;
case CMD_wnext:
case CMD_wNext:
case CMD_wprevious:
if (cmd[1] == 'n')
i = curwin->w_arg_idx + (int)line2;
else
i = curwin->w_arg_idx - (int)line2;
line1 = 1;
line2 = curbuf->b_ml.ml_line_count;
if (do_write(arg, FALSE) == FAIL)
break;
goto donextfile;
case CMD_next:
case CMD_snext:
do_next:
/*
* check for changed buffer now, if this fails the
* argument list is not redefined.
*/
if (!(p_hid || cmdidx == CMD_snext) &&
check_changed(curbuf, TRUE, FALSE))
break;
if (*arg != NUL) /* redefine file list */
{
if (do_arglist(arg) == FAIL)
break;
i = 0;
}
else
i = curwin->w_arg_idx + (int)line2;
donextfile: if (i < 0 || i >= arg_count)
{
if (arg_count <= 1)
EMSG("There is only one file to edit");
else if (i < 0)
EMSG("Cannot go before first file");
else
EMSG("Cannot go beyond last file");
break;
}
setpcmark();
if (*cmd == 's') /* split window first */
{
if (win_split(0, FALSE) == FAIL)
break;
}
else
{
register int other;
/*
* if 'hidden' set, only check for changed file when
* re-editing the same buffer
*/
other = TRUE;
if (p_hid)
other = otherfile(fix_fname(arg_files[i]));
if ((!p_hid || !other) &&
check_changed(curbuf, TRUE, !other))
break;
}
curwin->w_arg_idx = i;
if (i == arg_count - 1)
arg_had_last = TRUE;
(void)do_ecmd(0, arg_files[curwin->w_arg_idx],
NULL, do_ecmd_cmd, p_hid, do_ecmd_lnum, FALSE);
break;
case CMD_previous:
case CMD_sprevious:
case CMD_Next:
case CMD_sNext:
i = curwin->w_arg_idx - (int)line2;
goto donextfile;
case CMD_rewind:
case CMD_srewind:
i = 0;
goto donextfile;
case CMD_last:
case CMD_slast:
i = arg_count - 1;
goto donextfile;
case CMD_argument:
case CMD_sargument:
if (addr_count)
i = line2 - 1;
else
i = curwin->w_arg_idx;
goto donextfile;
case CMD_all:
case CMD_sall:
if (addr_count == 0)
line2 = 9999;
do_arg_all((int)line2); /* open a window for each argument */
break;
case CMD_buffer: /* :[N]buffer [N] to buffer N */
case CMD_sbuffer: /* :[N]sbuffer [N] to buffer N */
if (*arg)
{
errormsg = e_trailing;
break;
}
if (addr_count == 0) /* default is current buffer */
(void)do_buffer(*cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
DOBUF_CURRENT, FORWARD, 0, 0);
else
(void)do_buffer(*cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
DOBUF_FIRST, FORWARD, (int)line2, 0);
break;
case CMD_bmodified: /* :[N]bmod [N] to next modified buffer */
case CMD_sbmodified: /* :[N]sbmod [N] to next modified buffer */
(void)do_buffer(*cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
DOBUF_MOD, FORWARD, (int)line2, 0);
break;
case CMD_bnext: /* :[N]bnext [N] to next buffer */
case CMD_sbnext: /* :[N]sbnext [N] to next buffer */
(void)do_buffer(*cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
DOBUF_CURRENT, FORWARD, (int)line2, 0);
break;
case CMD_bNext: /* :[N]bNext [N] to previous buffer */
case CMD_bprevious: /* :[N]bprevious [N] to previous buffer */
case CMD_sbNext: /* :[N]sbNext [N] to previous buffer */
case CMD_sbprevious: /* :[N]sbprevious [N] to previous buffer */
(void)do_buffer(*cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
DOBUF_CURRENT, BACKWARD, (int)line2, 0);
break;
case CMD_brewind: /* :brewind to first buffer */
case CMD_sbrewind: /* :sbrewind to first buffer */
(void)do_buffer(*cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
DOBUF_FIRST, FORWARD, 0, 0);
break;
case CMD_blast: /* :blast to last buffer */
case CMD_sblast: /* :sblast to last buffer */
(void)do_buffer(*cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
DOBUF_LAST, FORWARD, 0, 0);
break;
case CMD_bunload: /* :[N]bunload[!] [N] [bufname] unload buffer */
case CMD_bdelete: /* :[N]bdelete[!] [N] [bufname] delete buffer */
errormsg = do_bufdel(
cmdidx == CMD_bdelete ? DOBUF_DEL : DOBUF_UNLOAD,
arg, addr_count, (int)line1, (int)line2, forceit);
break;
case CMD_unhide:
case CMD_sunhide: /* open a window for loaded buffers */
if (addr_count == 0)
line2 = 9999;
(void)do_buffer_all((int)line2, FALSE);
break;
case CMD_ball:
case CMD_sball: /* open a window for every buffer */
if (addr_count == 0)
line2 = 9999;
(void)do_buffer_all((int)line2, TRUE);
break;
case CMD_buffers:
case CMD_files:
case CMD_ls:
buflist_list();
break;
case CMD_write:
if (usefilter) /* input lines to shell command */
do_bang(1, line1, line2, FALSE, arg, TRUE, FALSE);
else
(void)do_write(arg, append);
break;
/*
* set screen mode
* if no argument given, just get the screen size and redraw
*/
case CMD_mode:
if (*arg == NUL || mch_screenmode(arg) != FAIL)
set_winsize(0, 0, FALSE);
break;
/*
* set, increment or decrement current window height
*/
case CMD_resize:
n = atol((char *)arg);
if (*arg == '-' || *arg == '+')
win_setheight(curwin->w_height + (int)n);
else
{
if (n == 0) /* default is very high */
n = 9999;
win_setheight((int)n);
}
break;
/*
* :sview [+command] file split window with new file, ro
* :split [[+command] file] split window with current or new file
* :new [[+command] file] split window with no or new file
*/
case CMD_sview:
case CMD_split:
case CMD_new:
old_curwin = curwin;
if (win_split(addr_count ? (int)line2 : 0, FALSE) == FAIL)
break;
/*FALLTHROUGH*/
case CMD_edit:
case CMD_ex:
case CMD_visual:
case CMD_view:
if ((cmdidx == CMD_new) && *arg == NUL)
{
setpcmark();
(void)do_ecmd(0, NULL, NULL, do_ecmd_cmd, TRUE,
(linenr_t)1, FALSE);
}
else if (cmdidx != CMD_split || *arg != NUL)
{
n = readonlymode;
if (cmdidx == CMD_view || cmdidx == CMD_sview)
readonlymode = TRUE;
setpcmark();
(void)do_ecmd(0, arg, NULL, do_ecmd_cmd, p_hid,
do_ecmd_lnum, FALSE);
readonlymode = n;
}
else
updateScreen(NOT_VALID);
/* if ":split file" worked, set alternate filename in
* old window to new file */
if ((cmdidx == CMD_new || cmdidx == CMD_split) &&
*arg != NUL && curwin != old_curwin &&
old_curwin->w_buffer != curbuf)
old_curwin->w_alt_fnum = curbuf->b_fnum;
break;
#ifdef USE_GUI
/*
* Change from the terminal version to the GUI version. File names may
* be given to redefine the args list -- webb
*/
case CMD_gvim:
case CMD_gui:
if (arg[0] == '-' && arg[1] == 'f' &&
(arg[2] == NUL || vim_iswhite(arg[2])))
{
gui.dofork = FALSE;
arg = skipwhite(arg + 2);
}
else
gui.dofork = TRUE;
if (!gui.in_use)
gui_start();
if (*arg != NUL && *arg != '|' && *arg != '\n')
goto do_next;
break;
#endif
case CMD_file:
do_file(arg, forceit);
break;
case CMD_swapname:
if (curbuf->b_ml.ml_mfp == NULL ||
(p = curbuf->b_ml.ml_mfp->mf_fname) == NULL)
MSG("No swap file");
else
msg(p);
break;
case CMD_mfstat: /* print memfile statistics, for debugging */
mf_statistics();
break;
case CMD_read:
if (usefilter) /* :r!cmd */
{
do_bang(1, line1, line2, FALSE, arg, FALSE, TRUE);
break;
}
if (u_save(line2, (linenr_t)(line2 + 1)) == FAIL)
break;
if (*arg == NUL)
{
if (check_fname() == FAIL) /* check for no file name */
break;
i = readfile(curbuf->b_filename, curbuf->b_sfilename,
line2, FALSE, (linenr_t)0, MAXLNUM, FALSE);
}
else
{
i = readfile(arg, NULL,
line2, FALSE, (linenr_t)0, MAXLNUM, FALSE);
}
if (i == FAIL)
{
emsg2(e_notopen, arg);
break;
}
updateScreen(NOT_VALID);
break;
case CMD_cd:
case CMD_chdir:
#ifdef UNIX
/*
* for UNIX ":cd" means: go to home directory
*/
if (*arg == NUL) /* use NameBuff for home directory name */
{
expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
arg = NameBuff;
}
#endif
if (*arg != NUL)
{
if (!did_cd)
{
BUF *buf;
/* use full path from now on for names of files
* being edited and swap files */
for (buf = firstbuf; buf != NULL; buf = buf->b_next)
{
buf->b_xfilename = buf->b_filename;
mf_fullname(buf->b_ml.ml_mfp);
}
status_redraw_all();
}
did_cd = TRUE;
if (vim_chdir((char *)arg))
emsg(e_failed);
break;
}
/*FALLTHROUGH*/
case CMD_pwd:
if (mch_dirname(NameBuff, MAXPATHL) == OK)
msg(NameBuff);
else
emsg(e_unknown);
break;
case CMD_equal:
smsg((char_u *)"line %ld", (long)line2);
break;
case CMD_list:
i = curwin->w_p_list;
curwin->w_p_list = 1;
case CMD_number: /* :nu */
case CMD_pound: /* :# */
case CMD_print: /* :p */
for ( ;!got_int; mch_breakcheck())
{
print_line(line1,
(cmdidx == CMD_number || cmdidx == CMD_pound));
if (++line1 > line2)
break;
flushbuf(); /* show one line at a time */
}
setpcmark();
curwin->w_cursor.lnum = line2; /* put cursor at last line */
if (cmdidx == CMD_list)
curwin->w_p_list = i;
break;
case CMD_shell:
do_shell(NULL);
break;
case CMD_sleep:
n = curwin->w_winpos + curwin->w_row - msg_scrolled;
if (n >= 0)
{
windgoto((int)n, curwin->w_col);
flushbuf();
}
mch_delay(line2 * 1000L, TRUE);
break;
case CMD_stag:
postponed_split = TRUE;
/*FALLTHROUGH*/
case CMD_tag:
do_tag(arg, 0, addr_count ? (int)line2 : 1);
break;
case CMD_pop:
do_tag((char_u *)"", 1, addr_count ? (int)line2 : 1);
break;
case CMD_tags:
do_tags();
break;
case CMD_marks:
do_marks(arg);
break;
case CMD_jumps:
do_jumps();
break;
case CMD_ascii:
do_ascii();
break;
case CMD_checkpath:
find_pattern_in_path(NULL, 0, FALSE, FALSE, CHECK_PATH, 1L,
forceit ? ACTION_SHOW_ALL : ACTION_SHOW,
(linenr_t)1, (linenr_t)MAXLNUM);
break;
case CMD_digraphs:
#ifdef DIGRAPHS
if (*arg)
putdigraph(arg);
else
listdigraphs();
#else
EMSG("No digraphs in this version");
#endif /* DIGRAPHS */
break;
case CMD_set:
(void)do_set(arg);
break;
case CMD_fixdel:
do_fixdel();
break;
#ifdef AUTOCMD
case CMD_autocmd:
/*
* Disallow auto commands from .exrc and .vimrc in current
* directory for security reasons.
*/
if (secure)
{
secure = 2;
errormsg = e_curdir;
}
else
do_autocmd(arg, forceit); /* handle the auto commands */
break;
case CMD_doautocmd:
do_doautocmd(arg); /* apply the automatic commands */
do_modelines();
break;
#endif
case CMD_abbreviate:
case CMD_cabbrev:
case CMD_iabbrev:
case CMD_cnoreabbrev:
case CMD_inoreabbrev:
case CMD_noreabbrev:
case CMD_unabbreviate:
case CMD_cunabbrev:
case CMD_iunabbrev:
i = ABBREV;
goto doabbr; /* almost the same as mapping */
case CMD_nmap:
case CMD_vmap:
case CMD_cmap:
case CMD_imap:
case CMD_map:
case CMD_nnoremap:
case CMD_vnoremap:
case CMD_cnoremap:
case CMD_inoremap:
case CMD_noremap:
/*
* If we are sourcing .exrc or .vimrc in current directory we
* print the mappings for security reasons.
*/
if (secure)
{
secure = 2;
msg_outtrans(cmd);
msg_outchar('\n');
}
case CMD_nunmap:
case CMD_vunmap:
case CMD_cunmap:
case CMD_iunmap:
case CMD_unmap:
i = 0;
doabbr:
if (*cmd == 'c') /* cmap, cunmap, cnoremap, etc. */
{
i += CMDLINE;
++cmd;
}
else if (*cmd == 'i') /* imap, iunmap, inoremap, etc. */
{
i += INSERT;
++cmd;
}
/* nmap, nunmap, nnoremap */
else if (*cmd == 'n' && *(cmd + 1) != 'o')
{
i += NORMAL;
++cmd;
}
else if (*cmd == 'v') /* vmap, vunmap, vnoremap */
{
i += VISUAL;
++cmd;
}
else if (forceit || i) /* map!, unmap!, noremap!, abbrev */
i += INSERT + CMDLINE;
else /* map, unmap, noremap */
i += NORMAL + VISUAL;
switch (do_map((*cmd == 'n') ? 2 : (*cmd == 'u'), arg, i))
{
case 1: emsg(e_invarg);
break;
case 2: emsg(e_nomap);
break;
case 3: emsg(e_ambmap);
break;
}
break;
case CMD_mapclear:
case CMD_imapclear:
case CMD_nmapclear:
case CMD_vmapclear:
case CMD_cmapclear:
map_clear(*cmd, forceit, FALSE);
break;
case CMD_abclear:
case CMD_iabclear:
case CMD_cabclear:
map_clear(*cmd, FALSE, TRUE);
break;
#ifdef USE_GUI
case CMD_menu: case CMD_noremenu: case CMD_unmenu:
case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu:
case CMD_vmenu: case CMD_vnoremenu: case CMD_vunmenu:
case CMD_imenu: case CMD_inoremenu: case CMD_iunmenu:
case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu:
gui_do_menu(cmd, arg, forceit);
break;
#endif /* USE_GUI */
case CMD_display:
case CMD_registers:
do_dis(arg); /* display buffer contents */
break;
case CMD_help:
do_help(arg);
break;
case CMD_version:
do_version(arg);
break;
case CMD_winsize: /* obsolete command */
line1 = getdigits(&arg);
arg = skipwhite(arg);
line2 = getdigits(&arg);
set_winsize((int)line1, (int)line2, TRUE);
break;
case CMD_delete:
case CMD_yank:
case CMD_rshift:
case CMD_lshift:
yankbuffer = regname;
curbuf->b_op_start.lnum = line1;
curbuf->b_op_end.lnum = line2;
op_line_count = line2 - line1 + 1;
op_motion_type = MLINE;
if (cmdidx != CMD_yank) /* set cursor position for undo */
{
setpcmark();
curwin->w_cursor.lnum = line1;
beginline(MAYBE);
}
switch (cmdidx)
{
case CMD_delete:
do_delete();
break;
case CMD_yank:
(void)do_yank(FALSE, TRUE);
break;
#ifdef RIGHTLEFT
case CMD_rshift:
do_shift(curwin->w_p_rl ? LSHIFT : RSHIFT, FALSE, amount);
break;
case CMD_lshift:
do_shift(curwin->w_p_rl ? RSHIFT : LSHIFT, FALSE, amount);
break;
#else
case CMD_rshift:
do_shift(RSHIFT, FALSE, amount);
break;
case CMD_lshift:
do_shift(LSHIFT, FALSE, amount);
break;
#endif
}
break;
case CMD_put:
yankbuffer = regname;
curwin->w_cursor.lnum = line2;
do_put(forceit ? BACKWARD : FORWARD, -1L, FALSE);
break;
case CMD_t:
case CMD_copy:
case CMD_move:
n = get_address(&arg);
if (arg == NULL) /* error detected */
{
nextcomm = NULL;
break;
}
/*
* move or copy lines from 'line1'-'line2' to below line 'n'
*/
if (n == MAXLNUM || n < 0 || n > curbuf->b_ml.ml_line_count)
{
emsg(e_invaddr);
break;
}
if (cmdidx == CMD_move)
{
if (do_move(line1, line2, n) == FAIL)
break;
}
else
do_copy(line1, line2, n);
u_clearline();
beginline(MAYBE);
updateScreen(NOT_VALID);
break;
case CMD_and: /* :& */
case CMD_tilde: /* :~ */
case CMD_substitute: /* :s */
do_sub(line1, line2, arg, &nextcomm,
cmdidx == CMD_substitute ? 0 :
cmdidx == CMD_and ? 1 : 2);
break;
case CMD_join:
curwin->w_cursor.lnum = line1;
if (line1 == line2)
{
if (addr_count >= 2) /* :2,2join does nothing */
break;
if (line2 == curbuf->b_ml.ml_line_count)
{
beep_flush();
break;
}
++line2;
}
do_do_join(line2 - line1 + 1, !forceit, FALSE);
beginline(TRUE);
break;
case CMD_global:
if (forceit)
*cmd = 'v';
case CMD_vglobal:
do_glob(*cmd, line1, line2, arg);
break;
case CMD_at: /* :[addr]@r */
curwin->w_cursor.lnum = line2;
/* put the register in mapbuf */
if (do_execbuf(*arg, TRUE,
vim_strchr(p_cpo, CPO_EXECBUF) != NULL) == FAIL)
beep_flush();
else
/* execute from the mapbuf */
while (vpeekc() == ':')
{
(void)vgetc();
(void)do_cmdline((char_u *)NULL, TRUE, TRUE);
}
break;
case CMD_bang:
do_bang(addr_count, line1, line2, forceit, arg, TRUE, TRUE);
break;
case CMD_undo:
u_undo(1);
break;
case CMD_redo:
u_redo(1);
break;
case CMD_source:
if (forceit) /* :so! read vi commands */
(void)openscript(arg);
/* :so read ex commands */
else if (do_source(arg, FALSE) == FAIL)
emsg2(e_notopen, arg);
break;
#ifdef VIMINFO
case CMD_rviminfo:
p = p_viminfo;
if (*p_viminfo == NUL)
p_viminfo = (char_u *)"'100";
if (read_viminfo(arg, TRUE, TRUE, forceit) == FAIL)
EMSG("Cannot open viminfo file for reading");
p_viminfo = p;
break;
case CMD_wviminfo:
p = p_viminfo;
if (*p_viminfo == NUL)
p_viminfo = (char_u *)"'100";
write_viminfo(arg, forceit);
p_viminfo = p;
break;
#endif /* VIMINFO */
case CMD_mkvimrc:
if (*arg == NUL)
arg = (char_u *)VIMRC_FILE;
/*FALLTHROUGH*/
case CMD_mkexrc:
{
FILE *fd;
if (*arg == NUL)
arg = (char_u *)EXRC_FILE;
#ifdef UNIX
/* with Unix it is possible to open a directory */
if (mch_isdir(arg))
{
EMSG2("\"%s\" is a directory", arg);
break;
}
#endif
if (!forceit && vim_fexists(arg))
{
EMSG2("\"%s\" exists (use ! to override)", arg);
break;
}
if ((fd = fopen((char *)arg, WRITEBIN)) == NULL)
{
EMSG2("Cannot open \"%s\" for writing", arg);
break;
}
/* Write the version command for :mkvimrc */
if (cmdidx == CMD_mkvimrc)
{
#ifdef USE_CRNL
fprintf(fd, "version 4.0\r\n");
#else
fprintf(fd, "version 4.0\n");
#endif
}
if (makemap(fd) == FAIL || makeset(fd) == FAIL ||
fclose(fd))
emsg(e_write);
break;
}
case CMD_cc:
qf_jump(0, addr_count ? (int)line2 : 0);
break;
case CMD_cfile:
if (*arg != NUL)
{
/*
* Great trick: Insert 'ef=' before arg.
* Always ok, because "cf " must be there.
*/
arg -= 3;
arg[0] = 'e';
arg[1] = 'f';
arg[2] = '=';
(void)do_set(arg);
}
if (qf_init() == OK)
qf_jump(0, 0); /* display first error */
break;
case CMD_clist:
qf_list(forceit);
break;
case CMD_cnext:
qf_jump(FORWARD, addr_count ? (int)line2 : 1);
break;
case CMD_cNext:
case CMD_cprevious:
qf_jump(BACKWARD, addr_count ? (int)line2 : 1);
break;
case CMD_cquit:
getout(1); /* this does not always work. why? */
case CMD_mark:
case CMD_k:
pos = curwin->w_cursor; /* save curwin->w_cursor */
curwin->w_cursor.lnum = line2;
beginline(MAYBE);
(void)setmark(*arg); /* set mark */
curwin->w_cursor = pos; /* restore curwin->w_cursor */
break;
case CMD_center:
case CMD_right:
case CMD_left:
do_align(line1, line2, atoi((char *)arg),
cmdidx == CMD_center ? 0 : cmdidx == CMD_right ? 1 : -1);
break;
case CMD_retab:
n = getdigits(&arg);
do_retab(line1, line2, (int)n, forceit);
u_clearline();
updateScreen(NOT_VALID);
break;
case CMD_make:
do_make(arg);
break;
/*
* :normal[!] {commands} - execute normal mode commands
* Mostly used for ":autocmd".
*/
case CMD_normal:
/*
* Stuff the argument into the typeahead buffer.
* Execute normal() until there is no more typeahead than
* there was before this command.
*/
len = typelen;
ins_typebuf(arg, forceit ? -1 : 0, 0, TRUE);
while ((!stuff_empty() ||
(!typebuf_typed() && typelen > len)) && !got_int)
{
adjust_cursor(); /* put cursor on an existing line */
cursupdate(); /* update cursor position */
normal(); /* get and execute a normal mode command */
}
break;
case CMD_isearch:
case CMD_dsearch:
i = ACTION_SHOW;
goto find_pat;
case CMD_ilist:
case CMD_dlist:
i = ACTION_SHOW_ALL;
goto find_pat;
case CMD_ijump:
case CMD_djump:
i = ACTION_GOTO;
goto find_pat;
case CMD_isplit:
case CMD_dsplit:
i = ACTION_SPLIT;
find_pat:
{
int whole = TRUE;
n = 1;
if (isdigit(*arg)) /* get count */
{
n = getdigits(&arg);
arg = skipwhite(arg);
}
if (*arg == '/') /* Match regexp, not just whole words */
{
whole = FALSE;
++arg;
for (p = arg; *p && *p != '/'; p++)
if (*p == '\\' && p[1] != NUL)
p++;
if (*p)
{
*p++ = NUL;
p = skipwhite(p);
/* Check for trailing illegal characters */
if (*p && vim_strchr((char_u *)"|\"\n", *p) == NULL)
errormsg = e_trailing;
else
nextcomm = p;
}
}
find_pattern_in_path(arg, (int)STRLEN(arg), whole, !forceit,
*cmd == 'd' ? FIND_DEFINE : FIND_ANY,
n, i, line1, line2);
}
break;
default:
/* Normal illegal commands have already been handled */
errormsg = (char_u *)"Sorry, this command is not implemented";
}
doend:
if (errormsg != NULL)
{
emsg(errormsg);
if (sourcing)
{
MSG_OUTSTR(": ");
msg_outtrans(*cmdlinep);
}
}
if (did_emsg)
nextcomm = NULL; /* cancel nextcomm at an error */
forceit = FALSE; /* reset now so it can be used in getfile() */
if (nextcomm && *nextcomm == NUL) /* not really a next command */
nextcomm = NULL;
return nextcomm;
}
/*
* If 'autowrite' option set, try to write the file.
*
* return FAIL for failure, OK otherwise
*/
int
autowrite(buf)
BUF *buf;
{
if (!p_aw || (!forceit && buf->b_p_ro) || buf->b_filename == NULL)
return FAIL;
return buf_write_all(buf);
}
/*
* flush all buffers, except the ones that are readonly
*/
void
autowrite_all()
{
BUF *buf;
if (!p_aw)
return;
for (buf = firstbuf; buf; buf = buf->b_next)
if (buf->b_changed && !buf->b_p_ro)
(void)buf_write_all(buf);
}
/*
* flush the contents of a buffer, unless it has no file name
*
* return FAIL for failure, OK otherwise
*/
static int
buf_write_all(buf)
BUF *buf;
{
return (buf_write(buf, buf->b_filename, buf->b_sfilename,
(linenr_t)1, buf->b_ml.ml_line_count, 0, 0, TRUE, FALSE));
}
/*
* write current buffer to file 'fname'
* if 'append' is TRUE, append to the file
*
* if *fname == NUL write to current file
* if b_notedited is TRUE, check for overwriting current file
*
* return FAIL for failure, OK otherwise
*/
static int
do_write(fname, append)
char_u *fname;
int append;
{
int other;
char_u *sfname = NULL; /* init to shut up gcc */
if (*fname == NUL)
other = FALSE;
else
{
sfname = fname;
fname = fix_fname(fname);
other = otherfile(fname);
}
/*
* if we have a new file name put it in the list of alternate file names
*/
if (other)
setaltfname(fname, sfname, (linenr_t)1);
/*
* writing to the current file is not allowed in readonly mode
* and need a file name
*/
if (!other && (check_readonly() || check_fname() == FAIL))
return FAIL;
if (!other)
{
fname = curbuf->b_filename;
sfname = curbuf->b_sfilename;
/*
* Not writing the whole file is only allowed with '!'.
*/
if ((line1 != 1 || line2 != curbuf->b_ml.ml_line_count) &&
!forceit && !append && !p_wa)
{
EMSG("Use ! to write partial buffer");
return FAIL;
}
}
/*
* write to other file or b_notedited set or not writing the whole file:
* overwriting only allowed with '!'
*/
if ((other || curbuf->b_notedited) && !forceit &&
!append && !p_wa && vim_fexists(fname))
{ /* don't overwrite existing file */
#ifdef UNIX
/* with UNIX it is possible to open a directory */
if (mch_isdir(fname))
EMSG2("\"%s\" is a directory", fname);
else
#endif
emsg(e_exists);
return FAIL;
}
return (buf_write(curbuf, fname, sfname, line1, line2,
append, forceit, TRUE, FALSE));
}
/*
* start editing a new file
*
* fnum: file number; if zero use fname/sfname
* fname: the file name
* - full path if sfname used,
* - any file name if sfname is NULL
* - empty string to re-edit with the same file name (but may be
* in a different directory)
* - NULL to start an empty buffer
* sfname: the short file name (or NULL)
* command: the command to be executed after loading the file
* hide: if TRUE don't free the current buffer
* newlnum: put cursor on this line number (if possible)
* set_help: set b_help flag of (new) buffer before opening file
*
* return FAIL for failure, OK otherwise
*/
int
do_ecmd(fnum, fname, sfname, command, hide, newlnum, set_help)
int fnum;
char_u *fname;
char_u *sfname;
char_u *command;
int hide;
linenr_t newlnum;
int set_help;
{
int other_file; /* TRUE if editing another file */
int oldbuf = FALSE; /* TRUE if using existing buffer */
BUF *buf;
if (fnum != 0)
{
if (fnum == curbuf->b_fnum) /* file is already being edited */
return OK; /* nothing to do */
other_file = TRUE;
}
else
{
/* if no short name given, use fname for short name */
if (sfname == NULL)
sfname = fname;
#ifdef USE_FNAME_CASE
# ifdef USE_LONG_FNAME
if (USE_LONG_FNAME)
# endif
fname_case(sfname); /* set correct case for short filename */
#endif
if (fname == NULL)
other_file = TRUE;
/* there is no file name */
else if (*fname == NUL && curbuf->b_filename == NULL)
other_file = FALSE;
else
{
if (*fname == NUL) /* re-edit with same file name */
{
fname = curbuf->b_filename;
sfname = curbuf->b_sfilename;
}
fname = fix_fname(fname); /* may expand to full path name */
other_file = otherfile(fname);
}
}
/*
* if the file was changed we may not be allowed to abandon it
* - if we are going to re-edit the same file
* - or if we are the only window on this file and if hide is FALSE
*/
if ((!other_file || (curbuf->b_nwindows == 1 && !hide)) &&
check_changed(curbuf, FALSE, !other_file))
{
if (fnum == 0 && other_file && fname != NULL)
setaltfname(fname, sfname, (linenr_t)1);
return FAIL;
}
/*
* End Visual mode before switching to another buffer, so the text can be
* copied into the GUI selection buffer.
*/
if (VIsual_active)
end_visual_mode();
/*
* If we are starting to edit another file, open a (new) buffer.
* Otherwise we re-use the current buffer.
*/
if (other_file)
{
curwin->w_alt_fnum = curbuf->b_fnum;
buflist_altlnum();
if (fnum)
buf = buflist_findnr(fnum);
else
buf = buflist_new(fname, sfname, 1L, TRUE);
if (buf == NULL)
return FAIL;
if (buf->b_ml.ml_mfp == NULL) /* no memfile yet */
{
oldbuf = FALSE;
buf->b_nwindows = 1;
}
else /* existing memfile */
{
oldbuf = TRUE;
++buf->b_nwindows;
buf_check_timestamp(buf);
}
/*
* make the (new) buffer the one used by the current window
* if the old buffer becomes unused, free it if hide is FALSE
* If the current buffer was empty and has no file name, curbuf
* is returned by buflist_new().
*/
if (buf != curbuf)
{
#ifdef AUTOCMD
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL);
#endif
#ifdef VIMINFO
curbuf->b_last_cursor = curwin->w_cursor;
#endif
buf_copy_options(curbuf, buf, TRUE);
close_buffer(curwin, curbuf, !hide, FALSE);
curwin->w_buffer = buf;
curbuf = buf;
}
curwin->w_pcmark.lnum = 1;
curwin->w_pcmark.col = 0;
}
else if (check_fname() == FAIL)
return FAIL;
/*
* If we get here we are sure to start editing
*/
/* don't redraw until the cursor is in the right line */
++RedrawingDisabled;
if (set_help)
curbuf->b_help = TRUE;
/*
* other_file oldbuf
* FALSE FALSE re-edit same file, buffer is re-used
* FALSE TRUE not posible
* TRUE FALSE start editing new file, new buffer
* TRUE TRUE start editing in existing buffer (nothing to do)
*/
if (!other_file) /* re-use the buffer */
{
if (newlnum == 0)
newlnum = curwin->w_cursor.lnum;
buf_freeall(curbuf); /* free all things for buffer */
buf_clear(curbuf);
curbuf->b_op_start.lnum = 0; /* clear '[ and '] marks */
curbuf->b_op_end.lnum = 0;
}
/*
* Check if we are editing the w_arg_idx file in the argument list.
*/
check_arg_idx();
if (!oldbuf) /* need to read the file */
(void)open_buffer();
#ifdef AUTOCMD
else
apply_autocmds(EVENT_BUFENTER, NULL, NULL);
#endif
win_init(curwin);
maketitle();
if (command == NULL)
{
if (newlnum)
{
curwin->w_cursor.lnum = newlnum;
check_cursor();
beginline(MAYBE);
}
else
beginline(TRUE);
}
/*
* Did not read the file, need to show some info about the file.
* Do this after setting the cursor.
*/
if (oldbuf)
fileinfo(did_cd, TRUE, FALSE);
if (command != NULL)
do_cmdline(command, TRUE, FALSE);
--RedrawingDisabled;
if (!skip_redraw)
updateScreen(CURSUPD); /* redraw now */
if (p_im)
need_start_insertmode = TRUE;
return OK;
}
/*
* get + command from ex argument
*/
static char_u *
getargcmd(argp)
char_u **argp;
{
char_u *arg = *argp;
char_u *command = NULL;
if (*arg == '+') /* +[command] */
{
++arg;
if (vim_isspace(*arg))
command = (char_u *)"$";
else
{
/*
* should check for "\ " (but vi has a bug that prevents it to work)
*/
command = arg;
arg = skiptowhite(command);
if (*arg)
*arg++ = NUL; /* terminate command with NUL */
}
arg = skipwhite(arg); /* skip over spaces */
*argp = arg;
}
return command;
}
/*
* Halve the number of backslashes in a file name argument.
* For MS-DOS we only do this if the character after the backslash
* is not a normal file character.
* For Unix, when wildcards are going to be expanded, don't remove
* backslashes before special characters.
*/
static void
backslash_halve(p, expand_wildcards)
char_u *p;
int expand_wildcards; /* going to expand wildcards later */
{
for ( ; *p; ++p)
if (is_backslash(p)
#if defined(MSDOS) || defined(WIN32)
&& p[1] != '*' && p[1] != '?'
#endif
#if defined(UNIX) || defined(OS2)
&& !(expand_wildcards &&
vim_strchr((char_u *)" *?[{`$\\", p[1]))
#endif
)
STRCPY(p, p + 1);
}
static void
do_make(arg)
char_u *arg;
{
if (*p_ef == NUL)
{
EMSG("errorfile option not set");
return;
}
autowrite_all();
vim_remove(p_ef);
sprintf((char *)IObuff, "%s %s %s", arg, p_sp, p_ef);
MSG_OUTSTR(":!");
msg_outtrans(IObuff); /* show what we are doing */
do_shell(IObuff);
#ifdef AMIGA
flushbuf();
/* read window status report and redraw before message */
(void)char_avail();
#endif
if (qf_init() == OK)
qf_jump(0, 0); /* display first error */
vim_remove(p_ef);
}
/*
* Redefine the argument list to 'str'.
*
* Return FAIL for failure, OK otherwise.
*/
static int
do_arglist(str)
char_u *str;
{
int new_count = 0;
char_u **new_files = NULL;
int exp_count;
char_u **exp_files;
char_u **t;
char_u *p;
int inquote;
int i;
while (*str)
{
/*
* create a new entry in new_files[]
*/
t = (char_u **)lalloc((long_u)(sizeof(char_u *) * (new_count + 1)), TRUE);
if (t != NULL)
for (i = new_count; --i >= 0; )
t[i] = new_files[i];
vim_free(new_files);
if (t == NULL)
return FAIL;
new_files = t;
new_files[new_count++] = str;
/*
* isolate one argument, taking quotes
*/
inquote = FALSE;
for (p = str; *str; ++str)
{
/*
* for MSDOS et.al. a backslash is part of a file name.
* Only skip ", space and tab.
*/
if (is_backslash(str))
*p++ = *++str;
else
{
if (!inquote && vim_isspace(*str))
break;
if (*str == '"')
inquote ^= TRUE;
else
*p++ = *str;
}
}
str = skipwhite(str);
*p = NUL;
}
i = ExpandWildCards(new_count, new_files, &exp_count,
&exp_files, FALSE, TRUE);
vim_free(new_files);
if (i == FAIL)
return FAIL;
if (exp_count == 0)
{
emsg(e_nomatch);
return FAIL;
}
if (arg_exp) /* arg_files[] has been allocated, free it */
FreeWild(arg_count, arg_files);
else
arg_exp = TRUE;
arg_files = exp_files;
arg_count = exp_count;
arg_had_last = FALSE;
/*
* put all file names in the buffer list
*/
for (i = 0; i < arg_count; ++i)
(void)buflist_add(arg_files[i]);
return OK;
}
/*
* Return TRUE if "str" starts with a backslash that should be removed.
* For MS-DOS, WIN32 and OS/2 this is only done when the character after the
* backslash is not a normal file name character.
*/
static int
is_backslash(str)
char_u *str;
{
#ifdef BACKSLASH_IN_FILENAME
return (str[0] == '\\' && str[1] != NUL &&
!(isfilechar(str[1]) && str[1] != '\\'));
#else
return (str[0] == '\\' && str[1] != NUL);
#endif
}
/*
* Check if we are editing the w_arg_idx file in the argument list.
*/
void
check_arg_idx()
{
int t;
if (arg_count > 1 && (curbuf->b_filename == NULL ||
curwin->w_arg_idx >= arg_count ||
(t = fullpathcmp(arg_files[curwin->w_arg_idx],
curbuf->b_filename)) == FPC_DIFF || t == FPC_DIFFX))
curwin->w_arg_idx_invalid = TRUE;
else
curwin->w_arg_idx_invalid = FALSE;
}
void
gotocmdline(clr)
int clr;
{
msg_start();
if (clr) /* clear the bottom line(s) */
msg_clr_eos(); /* will reset clear_cmdline */
windgoto(cmdline_row, 0);
}
static int
check_readonly()
{
if (!forceit && curbuf->b_p_ro)
{
emsg(e_readonly);
return TRUE;
}
return FALSE;
}
/*
* return TRUE if buffer was changed and cannot be abandoned.
*/
static int
check_changed(buf, checkaw, mult_win)
BUF *buf;
int checkaw; /* do autowrite if buffer was changed */
int mult_win; /* check also when several windows for the buffer */
{
if ( !forceit &&
buf->b_changed && (mult_win || buf->b_nwindows <= 1) &&
(!checkaw || autowrite(buf) == FAIL))
{
emsg(e_nowrtmsg);
return TRUE;
}
return FALSE;
}
/*
* return TRUE if any buffer was changed and cannot be abandoned.
* That changed buffer becomes the current buffer.
*/
static int
check_changed_any()
{
BUF *buf;
int save;
if (!forceit)
{
for (buf = firstbuf; buf != NULL; buf = buf->b_next)
{
if (buf->b_changed)
{
/* There must be a wait_return for this message, do_buffer
* will cause a redraw */
exiting = FALSE;
if (EMSG2("No write since last change for buffer \"%s\"",
buf->b_xfilename == NULL ? (char_u *)"No File" :
buf->b_xfilename))
{
save = no_wait_return;
no_wait_return = FALSE;
wait_return(FALSE);
no_wait_return = save;
}
(void)do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD,
buf->b_fnum, 0);
return TRUE;
}
}
}
return FALSE;
}
/*
* return FAIL if there is no filename, OK if there is one
* give error message for FAIL
*/
int
check_fname()
{
if (curbuf->b_filename == NULL)
{
emsg(e_noname);
return FAIL;
}
return OK;
}
/*
* - if there are more files to edit
* - and this is the last window
* - and forceit not used
* - and not repeated twice on a row
* return FAIL and give error message if 'message' TRUE
* return OK otherwise
*/
static int
check_more(message)
int message; /* when FALSE check only, no messages */
{
if (!forceit && only_one_window() && arg_count > 1 && !arg_had_last &&
quitmore == 0)
{
if (message)
{
EMSGN("%ld more files to edit", arg_count - curwin->w_arg_idx - 1);
quitmore = 2; /* next try to quit is allowed */
}
return FAIL;
}
return OK;
}
/*
* try to abandon current file and edit a new or existing file
* 'fnum' is the number of the file, if zero use fname/sfname
*
* return 1 for "normal" error, 2 for "not written" error, 0 for success
* -1 for succesfully opening another file
* 'lnum' is the line number for the cursor in the new file (if non-zero).
*/
int
getfile(fnum, fname, sfname, setpm, lnum)
int fnum;
char_u *fname;
char_u *sfname;
int setpm;
linenr_t lnum;
{
int other;
if (fnum == 0)
{
fname_expand(&fname, &sfname); /* make fname full path, set sfname */
other = otherfile(fname);
}
else
other = (fnum != curbuf->b_fnum);
if (other)
++no_wait_return; /* don't wait for autowrite message */
if (other && !forceit && curbuf->b_nwindows == 1 &&
!p_hid && curbuf->b_changed && autowrite(curbuf) == FAIL)
{
if (other)
--no_wait_return;
emsg(e_nowrtmsg);
return 2; /* file has been changed */
}
if (other)
--no_wait_return;
if (setpm)
setpcmark();
if (!other)
{
if (lnum != 0)
curwin->w_cursor.lnum = lnum;
check_cursor();
beginline(MAYBE);
return 0; /* it's in the same file */
}
if (do_ecmd(fnum, fname, sfname, NULL, p_hid, lnum, FALSE) == OK)
return -1; /* opened another file */
return 1; /* error encountered */
}
/*
* vim_strncpy()
*
* This is here because strncpy() does not guarantee successful results when
* the to and from strings overlap. It is only currently called from nextwild()
* which copies part of the command line to another part of the command line.
* This produced garbage when expanding files etc in the middle of the command
* line (on my terminal, anyway) -- webb.
*/
static void
vim_strncpy(to, from, len)
char_u *to;
char_u *from;
int len;
{
int i;
if (to <= from)
{
while (len-- && *from)
*to++ = *from++;
if (len >= 0)
*to = *from; /* Copy NUL */
}
else
{
for (i = 0; i < len; i++)
{
to++;
if (*from++ == NUL)
{
i++;
break;
}
}
for (; i > 0; i--)
*--to = *--from;
}
}
/*
* Return FALSE if this is not an appropriate context in which to do
* completion of anything, & TRUE if it is (even if there are no matches).
* For the caller, this means that the character is just passed through like a
* normal character (instead of being expanded). This allows :s/^I^D etc.
*/
static int
nextwild(type)
int type;
{
int i;
char_u *p1;
char_u *p2;
int oldlen;
int difflen;
int v;
if (cmd_numfiles == -1)
set_expand_context(cmdfirstc, cmdbuff);
if (expand_context == EXPAND_UNSUCCESSFUL)
{
beep_flush();
return OK; /* Something illegal on command line */
}
if (expand_context == EXPAND_NOTHING)
{
/* Caller can use the character as a normal char instead */
return FAIL;
}
expand_interactively = TRUE;
MSG_OUTSTR("..."); /* show that we are busy */
flushbuf();
i = expand_pattern - cmdbuff;
oldlen = cmdpos - i;
if (type == WILD_NEXT || type == WILD_PREV)
{
/*
* Get next/previous match for a previous expanded pattern.
*/
p2 = ExpandOne(NULL, NULL, 0, type);
}
else
{
/*
* Translate string into pattern and expand it.
*/
if ((p1 = addstar(&cmdbuff[i], oldlen)) == NULL)
p2 = NULL;
else
{
p2 = ExpandOne(p1, strnsave(&cmdbuff[i], oldlen),
WILD_HOME_REPLACE, type);
vim_free(p1);
}
}
if (p2 != NULL)
{
if (cmdlen + (difflen = STRLEN(p2) - oldlen) > cmdbufflen - 4)
v = realloc_cmdbuff(cmdlen + difflen);
else
v = OK;
if (v == OK)
{
vim_strncpy(&cmdbuff[cmdpos + difflen], &cmdbuff[cmdpos],
cmdlen - cmdpos);
STRNCPY(&cmdbuff[i], p2, STRLEN(p2));
cmdlen += difflen;
cmdpos += difflen;
}
vim_free(p2);
}
redrawcmd();
if (cmd_numfiles <= 0 && p2 == NULL)
beep_flush();
else if (cmd_numfiles == 1)
(void)ExpandOne(NULL, NULL, 0, WILD_FREE); /* free expanded pattern */
expand_interactively = FALSE; /* reset for next call */
return OK;
}
#define MAXSUFLEN 30 /* maximum length of a file suffix */
/*
* Do wildcard expansion on the string 'str'.
* Return a pointer to alloced memory containing the new string.
* Return NULL for failure.
*
* mode = WILD_FREE: just free previously expanded matches
* mode = WILD_EXPAND_FREE: normal expansion, do not keep matches
* mode = WILD_EXPAND_KEEP: normal expansion, keep matches
* mode = WILD_NEXT: use next match in multiple match, wrap to first
* mode = WILD_PREV: use previous match in multiple match, wrap to first
* mode = WILD_ALL: return all matches concatenated
* mode = WILD_LONGEST: return longest matched part
*
* options = WILD_LIST_NOTFOUND: list entries without a match
* options = WILD_HOME_REPLACE: do home_replace() for buffer names
*/
char_u *
ExpandOne(str, orig, options, mode)
char_u *str;
char_u *orig; /* original string which is expanded */
int options;
int mode;
{
char_u *ss = NULL;
static char_u **cmd_files = NULL; /* list of input files */
static int findex;
static char_u *orig_save = NULL; /* kept value of orig */
int i, found = 0;
int multmatch = FALSE;
long_u len;
char_u *setsuf;
int fnamelen, setsuflen;
char_u suf_buf[MAXSUFLEN];
char_u *p;
/*
* first handle the case of using an old match
*/
if (mode == WILD_NEXT || mode == WILD_PREV)
{
if (cmd_numfiles > 0)
{
if (mode == WILD_PREV)
{
if (findex == -1)
findex = cmd_numfiles;
--findex;
}
else /* mode == WILD_NEXT */
++findex;
/*
* When wrapping around, return the original string, set findex to
* -1.
*/
if (findex < 0)
{
if (orig_save == NULL)
findex = cmd_numfiles - 1;
else
findex = -1;
}
if (findex >= cmd_numfiles)
{
if (orig_save == NULL)
findex = 0;
else
findex = -1;
}
if (findex == -1)
return strsave(orig_save);
return strsave(cmd_files[findex]);
}
else
return NULL;
}
/* free old names */
if (cmd_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST)
{
FreeWild(cmd_numfiles, cmd_files);
cmd_numfiles = -1;
vim_free(orig_save);
orig_save = NULL;
}
findex = 0;
if (mode == WILD_FREE) /* only release file name */
return NULL;
if (cmd_numfiles == -1)
{
vim_free(orig_save);
orig_save = orig;
if (ExpandFromContext(str, &cmd_numfiles, &cmd_files, FALSE,
options) == FAIL)
/* error: do nothing */;
else if (cmd_numfiles == 0)
{
if (!expand_interactively)
emsg(e_nomatch);
}
else
{
/*
* If the pattern starts with a '~', replace the home diretory
* with '~' again.
*/
if (*str == '~' && (options & WILD_HOME_REPLACE))
{
for (i = 0; i < cmd_numfiles; ++i)
{
p = home_replace_save(NULL, cmd_files[i]);
if (p != NULL)
{
vim_free(cmd_files[i]);
cmd_files[i] = p;
}
}
}
/*
* Insert backslashes into a file name before a space, \, %, # and
* wildmatch characters, except '~'.
*/
if (expand_interactively &&
(expand_context == EXPAND_FILES ||
expand_context == EXPAND_BUFFERS ||
expand_context == EXPAND_DIRECTORIES))
{
for (i = 0; i < cmd_numfiles; ++i)
{
p = strsave_escaped(cmd_files[i],
#ifdef BACKSLASH_IN_FILENAME
(char_u *)" *?[{`$%#");
#else
(char_u *)" *?[{`$\\%#");
#endif
if (p != NULL)
{
vim_free(cmd_files[i]);
cmd_files[i] = p;
}
}
}
if (mode != WILD_ALL && mode != WILD_LONGEST)
{
if (cmd_numfiles > 1) /* more than one match; check suffix */
{
found = -2;
for (i = 0; i < cmd_numfiles; ++i)
{
fnamelen = STRLEN(cmd_files[i]);
setsuflen = 0;
for (setsuf = p_su; *setsuf; )
{
setsuflen = copy_option_part(&setsuf, suf_buf,
MAXSUFLEN, ".,");
if (fnamelen >= setsuflen && STRNCMP(suf_buf,
cmd_files[i] + fnamelen - setsuflen,
(size_t)setsuflen) == 0)
break;
setsuflen = 0;
}
if (setsuflen) /* suffix matched: ignore file */
continue;
if (found >= 0)
{
multmatch = TRUE;
break;
}
found = i;
}
}
if (multmatch || found < 0)
{
/* Can we ever get here unless it's while expanding
* interactively? If not, we can get rid of this all
* together. Don't really want to wait for this message
* (and possibly have to hit return to continue!).
*/
if (!expand_interactively)
emsg(e_toomany);
else
beep_flush();
found = 0; /* return first one */
multmatch = TRUE; /* for found < 0 */
}
if (found >= 0 && !(multmatch && mode == WILD_EXPAND_FREE))
ss = strsave(cmd_files[found]);
}
}
}
/* Find longest common part */
if (mode == WILD_LONGEST && cmd_numfiles > 0)
{
for (len = 0; cmd_files[0][len]; ++len)
{
for (i = 0; i < cmd_numfiles; ++i)
{
#ifdef CASE_INSENSITIVE_FILENAME
if ((expand_context == EXPAND_DIRECTORIES ||
expand_context == EXPAND_FILES ||
expand_context == EXPAND_BUFFERS) &&
toupper(cmd_files[i][len]) != toupper(cmd_files[0][len]))
break;
else
#endif
if (cmd_files[i][len] != cmd_files[0][len])
break;
}
if (i < cmd_numfiles)
{
vim_beep();
break;
}
}
ss = alloc((unsigned)len + 1);
if (ss)
{
STRNCPY(ss, cmd_files[0], len);
ss[len] = NUL;
}
findex = -1; /* next p_wc gets first one */
}
/* Concatenate all matching names */
if (mode == WILD_ALL && cmd_numfiles > 0)
{
len = 0;
for (i = 0; i < cmd_numfiles; ++i)
len += STRLEN(cmd_files[i]) + 1;
ss = lalloc(len, TRUE);
if (ss)
{
*ss = NUL;
for (i = 0; i < cmd_numfiles; ++i)
{
STRCAT(ss, cmd_files[i]);
if (i != cmd_numfiles - 1)
STRCAT(ss, " ");
}
}
}
if (mode == WILD_EXPAND_FREE || mode == WILD_ALL)
{
FreeWild(cmd_numfiles, cmd_files);
cmd_numfiles = -1;
}
return ss;
}
/*
* show all matches for completion on the command line
*/
static int
showmatches(buff)
char_u *buff;
{
char_u *file_str;
int num_files;
char_u **files_found;
int i, j, k;
int maxlen;
int lines;
int columns;
char_u *p;
int lastlen;
set_expand_context(cmdfirstc, cmdbuff);
if (expand_context == EXPAND_UNSUCCESSFUL)
{
beep_flush();
return OK; /* Something illegal on command line */
}
if (expand_context == EXPAND_NOTHING)
{
/* Caller can use the character as a normal char instead */
return FAIL;
}
expand_interactively = TRUE;
/* add star to file name, or convert to regexp if not expanding files! */
file_str = addstar(expand_pattern, (int)(buff + cmdpos - expand_pattern));
if (file_str == NULL)
{
expand_interactively = FALSE;
return OK;
}
msg_didany = FALSE; /* lines_left will be set */
msg_start(); /* prepare for paging */
msg_outchar('\n');
flushbuf();
cmdline_row = msg_row;
msg_didany = FALSE; /* lines_left will be set again */
msg_start(); /* prepare for paging */
/* find all files that match the description */
if (ExpandFromContext(file_str, &num_files, &files_found, FALSE, 0) == FAIL)
{
num_files = 0;
files_found = (char_u **)"";
}
/* find the length of the longest file name */
maxlen = 0;
for (i = 0; i < num_files; ++i)
{
if (expand_context == EXPAND_FILES || expand_context == EXPAND_BUFFERS)
{
home_replace(NULL, files_found[i], NameBuff, MAXPATHL);
j = strsize(NameBuff);
}
else
j = strsize(files_found[i]);
if (j > maxlen)
maxlen = j;
}
/* compute the number of columns and lines for the listing */
maxlen += 2; /* two spaces between file names */
columns = ((int)Columns + 2) / maxlen;
if (columns < 1)
columns = 1;
lines = (num_files + columns - 1) / columns;
(void)set_highlight('d'); /* find out highlight mode for directories */
/* list the files line by line */
for (i = 0; i < lines; ++i)
{
lastlen = 999;
for (k = i; k < num_files; k += lines)
{
for (j = maxlen - lastlen; --j >= 0; )
msg_outchar(' ');
if (expand_context == EXPAND_FILES ||
expand_context == EXPAND_BUFFERS)
{
/* highlight directories */
j = (mch_isdir(files_found[k]));
home_replace(NULL, files_found[k], NameBuff, MAXPATHL);
p = NameBuff;
}
else
{
j = FALSE;
p = files_found[k];
}
if (j)
start_highlight();
lastlen = msg_outtrans(p);
if (j)
stop_highlight();
}
msg_outchar('\n');
flushbuf(); /* show one line at a time */
if (got_int)
{
got_int = FALSE;
break;
}
}
vim_free(file_str);
FreeWild(num_files, files_found);
/*
* we redraw the command below the lines that we have just listed
* This is a bit tricky, but it saves a lot of screen updating.
*/
cmdline_row = msg_row; /* will put it back later */
expand_interactively = FALSE;
return OK;
}
/*
* Prepare a string for expansion.
* When expanding file names: The string will be used with ExpandWildCards().
* Copy the file name into allocated memory and add a '*' at the end.
* When expanding other names: The string will be used with regcomp(). Copy
* the name into allocated memory and add ".*" at the end.
*/
char_u *
addstar(fname, len)
char_u *fname;
int len;
{
char_u *retval;
int i, j;
int new_len;
char_u *tail;
if (expand_interactively && expand_context != EXPAND_FILES &&
expand_context != EXPAND_DIRECTORIES)
{
/*
* Matching will be done internally (on something other than files).
* So we convert the file-matching-type wildcards into our kind for
* use with vim_regcomp(). First work out how long it will be:
*/
/* for help tags the translation is done in find_help_tags() */
if (expand_context == EXPAND_HELP)
retval = strnsave(fname, len);
else
{
new_len = len + 2; /* +2 for '^' at start, NUL at end */
for (i = 0; i < len; i++)
{
if (fname[i] == '*' || fname[i] == '~')
new_len++; /* '*' needs to be replaced by ".*"
'~' needs to be replaced by "\~" */
/* Buffer names are like file names. "." should be literal */
if (expand_context == EXPAND_BUFFERS && fname[i] == '.')
new_len++; /* "." becomes "\." */
}
retval = alloc(new_len);
if (retval != NULL)
{
retval[0] = '^';
j = 1;
for (i = 0; i < len; i++, j++)
{
if (fname[i] == '\\' && ++i == len) /* skip backslash */
break;
switch (fname[i])
{
case '*': retval[j++] = '.';
break;
case '~': retval[j++] = '\\';
break;
case '?': retval[j] = '.';
continue;
case '.': if (expand_context == EXPAND_BUFFERS)
retval[j++] = '\\';
break;
}
retval[j] = fname[i];
}
retval[j] = NUL;
}
}
}
else
{
retval = alloc(len + 4);
if (retval != NULL)
{
STRNCPY(retval, fname, len);
retval[len] = NUL;
backslash_halve(retval, TRUE); /* remove some backslashes */
len = STRLEN(retval);
/*
* Don't add a star to ~, ~user, $var or `cmd`.
* ~ would be at the start of the tail.
* $ could be anywhere in the tail.
* ` could be anywhere in the file name.
*/
tail = gettail(retval);
if (*tail != '~' && vim_strchr(tail, '$') == NULL
&& vim_strchr(retval, '`') == NULL)
{
#ifdef MSDOS
/*
* if there is no dot in the file name, add "*.*" instead of
* "*".
*/
for (i = len - 1; i >= 0; --i)
if (vim_strchr((char_u *)".\\/:", retval[i]) != NULL)
break;
if (i < 0 || retval[i] != '.')
{
retval[len++] = '*';
retval[len++] = '.';
}
#endif
retval[len++] = '*';
}
retval[len] = NUL;
}
}
return retval;
}
/*
* do_source: read the file "fname" and execute its lines as EX commands
*
* This function may be called recursively!
*
* return FAIL if file could not be opened, OK otherwise
*/
int
do_source(fname, check_other)
register char_u *fname;
int check_other; /* check for .vimrc and _vimrc */
{
register FILE *fp;
register int len;
#ifdef USE_CRNL
int has_cr;
int textmode = -1; /* -1 = unknown, 0 = NL, 1 = CR-NL */
int error = FALSE;
#endif
/* use NameBuff for expanded name */
expand_env(fname, NameBuff, MAXPATHL);
fp = fopen((char *)NameBuff, READBIN);
if (fp == NULL && check_other)
{
/*
* Try again, replacing file name ".vimrc" by "_vimrc" or vice versa
* (if applicable)
*/
len = STRLEN(NameBuff);
if (((len > 6 && ispathsep(NameBuff[len - 7])) || len == 6) &&
(NameBuff[len - 6] == '.' || NameBuff[len - 6] == '_') &&
(STRCMP(&NameBuff[len - 5], "vimrc") == 0))
{
if (NameBuff[len - 6] == '_')
NameBuff[len - 6] = '.';
else
NameBuff[len - 6] = '_';
fp = fopen((char *)NameBuff, READBIN);
}
}
if (fp == NULL)
return FAIL;
#ifdef USE_CRNL
/* no automatic textmode: Set default to CR-NL */
if (!p_ta)
textmode = 1;
#endif
sourcing_name = fname;
sourcing_lnum = 1;
#ifdef SLEEP_IN_EMSG
++dont_sleep; /* don't call sleep() in emsg() */
#endif
len = 0;
while (fgets((char *)IObuff + len, IOSIZE - len, fp) != NULL && !got_int)
{
len = STRLEN(IObuff) - 1;
if (len >= 0 && IObuff[len] == '\n') /* remove trailing newline */
{
#ifdef USE_CRNL
has_cr = (len > 0 && IObuff[len - 1] == '\r');
if (textmode == -1)
{
if (has_cr)
textmode = 1;
else
textmode = 0;
}
if (textmode)
{
if (has_cr) /* remove trailing CR-LF */
--len;
else /* lines like ":map xx yy^M" will have failed */
{
if (!error)
EMSG("Warning: Wrong line separator, ^M may be missing");
error = TRUE;
textmode = 0;
}
}
#endif
/* escaped newline, read more */
if (len > 0 && len < IOSIZE && IObuff[len - 1] == Ctrl('V'))
{
IObuff[len - 1] = '\n'; /* remove CTRL-V */
++sourcing_lnum;
continue;
}
IObuff[len] = NUL;
}
/* check for ^C here, so recursive :so will be broken */
mch_breakcheck();
do_cmdline(IObuff, TRUE, TRUE);
len = 0;
++sourcing_lnum;
}
fclose(fp);
if (got_int)
emsg(e_interr);
#ifdef SLEEP_IN_EMSG
--dont_sleep;
#endif
sourcing_name = NULL;
sourcing_lnum = 0;
return OK;
}
/*
* get a single EX address
*
* Set ptr to the next character after the part that was interpreted.
* Set ptr to NULL when an error is encountered.
*/
static linenr_t
get_address(ptr)
char_u **ptr;
{
linenr_t cursor_lnum = curwin->w_cursor.lnum;
int c;
int i;
long n;
char_u *cmd;
FPOS pos;
FPOS *fp;
linenr_t lnum;
cmd = skipwhite(*ptr);
lnum = MAXLNUM;
do
{
switch (*cmd)
{
case '.': /* '.' - Cursor position */
++cmd;
lnum = cursor_lnum;
break;
case '$': /* '$' - last line */
++cmd;
lnum = curbuf->b_ml.ml_line_count;
break;
case '\'': /* ''' - mark */
if (*++cmd == NUL || (check_mark(
fp = getmark(*cmd++, FALSE)) == FAIL))
goto error;
lnum = fp->lnum;
break;
case '/':
case '?': /* '/' or '?' - search */
c = *cmd++;
pos = curwin->w_cursor; /* save curwin->w_cursor */
if (c == '/') /* forward search, start on next line */
{
++curwin->w_cursor.lnum;
curwin->w_cursor.col = 0;
}
else /* backward search, start on prev line */
{
--curwin->w_cursor.lnum;
curwin->w_cursor.col = MAXCOL;
}
searchcmdlen = 0;
if (!do_search(c, cmd, 1L,
SEARCH_HIS + SEARCH_MSG + SEARCH_START))
{
cmd = NULL;
curwin->w_cursor = pos;
goto error;
}
lnum = curwin->w_cursor.lnum;
curwin->w_cursor = pos;
/* adjust command string pointer */
cmd += searchcmdlen;
break;
case '\\': /* "\?", "\/" or "\&", repeat search */
++cmd;
if (*cmd == '&')
i = RE_SUBST;
else if (*cmd == '?' || *cmd == '/')
i = RE_SEARCH;
else
{
emsg(e_backslash);
cmd = NULL;
goto error;
}
/* forward search, start on next line */
if (*cmd != '?')
{
pos.lnum = curwin->w_cursor.lnum + 1;
pos.col = 0;
}
/* backward search, start on prev line */
else
{
pos.lnum = curwin->w_cursor.lnum - 1;
pos.col = MAXCOL;
}
if (searchit(&pos, *cmd == '?' ? BACKWARD : FORWARD,
(char_u *)"", 1L,
SEARCH_MSG + SEARCH_START, i) == OK)
lnum = pos.lnum;
else
{
cmd = NULL;
goto error;
}
++cmd;
break;
default:
if (isdigit(*cmd)) /* absolute line number */
lnum = getdigits(&cmd);
}
for (;;)
{
cmd = skipwhite(cmd);
if (*cmd != '-' && *cmd != '+' && !isdigit(*cmd))
break;
if (lnum == MAXLNUM)
lnum = cursor_lnum; /* "+1" is same as ".+1" */
if (isdigit(*cmd))
i = '+'; /* "number" is same as "+number" */
else
i = *cmd++;
if (!isdigit(*cmd)) /* '+' is '+1', but '+0' is not '+1' */
n = 1;
else
n = getdigits(&cmd);
if (i == '-')
lnum -= n;
else
lnum += n;
}
cursor_lnum = lnum;
} while (*cmd == '/' || *cmd == '?');
error:
*ptr = cmd;
return lnum;
}
/*
* Must parse the command line so far to work out what context we are in.
* Completion can then be done based on that context.
* This routine sets two global variables:
* char_u *expand_pattern The start of the pattern to be expanded within
* the command line (ends at the cursor).
* int expand_context The type of thing to expand. Will be one of:
*
* EXPAND_UNSUCCESSFUL Used sometimes when there is something illegal on
* the command line, like an unknown command. Caller
* should beep.
* EXPAND_NOTHING Unrecognised context for completion, use char like
* a normal char, rather than for completion. eg
* :s/^I/
* EXPAND_COMMANDS Cursor is still touching the command, so complete
* it.
* EXPAND_BUFFERS Complete file names for :buf and :sbuf commands.
* EXPAND_FILES After command with XFILE set, or after setting
* with P_EXPAND set. eg :e ^I, :w>>^I
* EXPAND_DIRECTORIES In some cases this is used instead of the latter
* when we know only directories are of interest. eg
* :set dir=^I
* EXPAND_SETTINGS Complete variable names. eg :set d^I
* EXPAND_BOOL_SETTINGS Complete boolean variables only, eg :set no^I
* EXPAND_TAGS Complete tags from the files in p_tags. eg :ta a^I
* EXPAND_HELP Complete tags from the file 'helpfile'/vim_tags
* EXPAND_EVENTS Complete event names
*
* -- webb.
*/
static void
set_expand_context(firstc, buff)
int firstc; /* either ':', '/', or '?' */
char_u *buff; /* buffer for command string */
{
char_u *nextcomm;
char_u old_char;
old_char = cmdbuff[cmdpos];
cmdbuff[cmdpos] = NUL;
nextcomm = buff;
while (nextcomm != NULL)
nextcomm = set_one_cmd_context(firstc, nextcomm);
cmdbuff[cmdpos] = old_char;
}
/*
* This is all pretty much copied from do_one_cmd(), with all the extra stuff
* we don't need/want deleted. Maybe this could be done better if we didn't
* repeat all this stuff. The only problem is that they may not stay perfectly
* compatible with each other, but then the command line syntax probably won't
* change that much -- webb.
*/
static char_u *
set_one_cmd_context(firstc, buff)
int firstc; /* either ':', '/', or '?' */
char_u *buff; /* buffer for command string */
{
char_u *p;
char_u *cmd, *arg;
int i;
int cmdidx;
long argt;
char_u delim;
int forced = FALSE;
int usefilter = FALSE; /* filter instead of file name */
expand_pattern = buff;
if (firstc != ':')
{
expand_context = EXPAND_NOTHING;
return NULL;
}
expand_context = EXPAND_COMMANDS; /* Default until we get past command */
/*
* 2. skip comment lines and leading space, colons or bars
*/
for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++)
;
expand_pattern = cmd;
if (*cmd == NUL)
return NULL;
if (*cmd == '"') /* ignore comment lines */
{
expand_context = EXPAND_NOTHING;
return NULL;
}
/*
* 3. parse a range specifier of the form: addr [,addr] [;addr] ..
*/
/*
* Backslashed delimiters after / or ? will be skipped, and commands will
* not be expanded between /'s and ?'s or after "'". -- webb
*/
while (*cmd != NUL && (vim_isspace(*cmd) || isdigit(*cmd) ||
vim_strchr((char_u *)".$%'/?-+,;", *cmd) != NULL))
{
if (*cmd == '\'')
{
if (*++cmd == NUL)
expand_context = EXPAND_NOTHING;
}
else if (*cmd == '/' || *cmd == '?')
{
delim = *cmd++;
while (*cmd != NUL && *cmd != delim)
if (*cmd++ == '\\' && *cmd != NUL)
++cmd;
if (*cmd == NUL)
expand_context = EXPAND_NOTHING;
}
if (*cmd != NUL)
++cmd;
}
/*
* 4. parse command
*/
cmd = skipwhite(cmd);
expand_pattern = cmd;
if (*cmd == NUL)
return NULL;
if (*cmd == '"')
{
expand_context = EXPAND_NOTHING;
return NULL;
}
if (*cmd == '|' || *cmd == '\n')
return cmd + 1; /* There's another command */
/*
* Isolate the command and search for it in the command table.
* Exeptions:
* - the 'k' command can directly be followed by any character.
* - the 's' command can be followed directly by 'c', 'g' or 'r'
*/
if (*cmd == 'k')
{
cmdidx = CMD_k;
p = cmd + 1;
}
else
{
p = cmd;
while (isalpha(*p) || *p == '*') /* Allow * wild card */
++p;
/* check for non-alpha command */
if (p == cmd && vim_strchr((char_u *)"@!=><&~#", *p) != NULL)
++p;
i = (int)(p - cmd);
if (i == 0)
{
expand_context = EXPAND_UNSUCCESSFUL;
return NULL;
}
for (cmdidx = 0; cmdidx < CMD_SIZE; ++cmdidx)
if (STRNCMP(cmdnames[cmdidx].cmd_name, cmd, (size_t)i) == 0)
break;
}
/*
* If the cursor is touching the command, and it ends in an alphabetic
* character, complete the command name.
*/
if (p == cmdbuff + cmdpos && isalpha(p[-1]))
return NULL;
if (cmdidx == CMD_SIZE)
{
if (*cmd == 's' && vim_strchr((char_u *)"cgr", cmd[1]) != NULL)
{
cmdidx = CMD_substitute;
p = cmd + 1;
}
else
{
/* Not still touching the command and it was an illegal command */
expand_context = EXPAND_UNSUCCESSFUL;
return NULL;
}
}
expand_context = EXPAND_NOTHING; /* Default now that we're past command */
if (*p == '!') /* forced commands */
{
forced = TRUE;
++p;
}
/*
* 5. parse arguments
*/
argt = cmdnames[cmdidx].cmd_argt;
arg = skipwhite(p);
if (cmdidx == CMD_write)
{
if (*arg == '>') /* append */
{
if (*++arg == '>') /* It should be */
++arg;
arg = skipwhite(arg);
}
else if (*arg == '!') /* :w !filter */
{
++arg;
usefilter = TRUE;
}
}
if (cmdidx == CMD_read)
{
usefilter = forced; /* :r! filter if forced */
if (*arg == '!') /* :r !filter */
{
++arg;
usefilter = TRUE;
}
}
if (cmdidx == CMD_lshift || cmdidx == CMD_rshift)
{
while (*arg == *cmd) /* allow any number of '>' or '<' */
++arg;
arg = skipwhite(arg);
}
/* Does command allow "+command"? */
if ((argt & EDITCMD) && !usefilter && *arg == '+')
{
/* Check if we're in the +command */
p = arg + 1;
arg = skiptowhite(arg);
/* Still touching the command after '+'? */
if (arg >= cmdbuff + cmdpos)
return p;
/* Skip space after +command to get to the real argument */
arg = skipwhite(arg);
}
/*
* Check for '|' to separate commands and '"' to start comments.
* Don't do this for ":read !cmd" and ":write !cmd".
*/
if ((argt & TRLBAR) && !usefilter)
{
p = arg;
while (*p)
{
if (*p == Ctrl('V'))
{
if (p[1] != NUL)
++p;
}
else if ((*p == '"' && !(argt & NOTRLCOM)) || *p == '|' || *p == '\n')
{
if (*(p - 1) != '\\')
{
if (*p == '|' || *p == '\n')
return p + 1;
return NULL; /* It's a comment */
}
}
++p;
}
}
/* no arguments allowed */
if (!(argt & EXTRA) && *arg != NUL &&
vim_strchr((char_u *)"|\"", *arg) == NULL)
return NULL;
/* Find start of last argument (argument just before cursor): */
p = cmdbuff + cmdpos;
while (p != arg && *p != ' ' && *p != TAB)
p--;
if (*p == ' ' || *p == TAB)
p++;
expand_pattern = p;
if (argt & XFILE)
{
int in_quote = FALSE;
char_u *bow = NULL; /* Beginning of word */
/*
* Allow spaces within back-quotes to count as part of the argument
* being expanded.
*/
expand_pattern = skipwhite(arg);
for (p = expand_pattern; *p; ++p)
{
if (*p == '\\' && p[1])
++p;
#ifdef SPACE_IN_FILENAME
else if (vim_iswhite(*p) && (!(argt & NOSPC) || usefilter))
#else
else if (vim_iswhite(*p))
#endif
{
p = skipwhite(p);
if (in_quote)
bow = p;
else
expand_pattern = p;
--p;
}
else if (*p == '`')
{
if (!in_quote)
{
expand_pattern = p;
bow = p + 1;
}
in_quote = !in_quote;
}
}
/*
* If we are still inside the quotes, and we passed a space, just
* expand from there.
*/
if (bow != NULL && in_quote)
expand_pattern = bow;
expand_context = EXPAND_FILES;
}
/*
* 6. switch on command name
*/
switch (cmdidx)
{
case CMD_cd:
case CMD_chdir:
expand_context = EXPAND_DIRECTORIES;
break;
case CMD_global:
case CMD_vglobal:
delim = *arg; /* get the delimiter */
if (delim)
++arg; /* skip delimiter if there is one */
while (arg[0] != NUL && arg[0] != delim)
{
if (arg[0] == '\\' && arg[1] != NUL)
++arg;
++arg;
}
if (arg[0] != NUL)
return arg + 1;
break;
case CMD_and:
case CMD_substitute:
delim = *arg;
if (delim)
++arg;
for (i = 0; i < 2; i++)
{
while (arg[0] != NUL && arg[0] != delim)
{
if (arg[0] == '\\' && arg[1] != NUL)
++arg;
++arg;
}
if (arg[0] != NUL) /* skip delimiter */
++arg;
}
while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL)
++arg;
if (arg[0] != NUL)
return arg;
break;
case CMD_isearch:
case CMD_dsearch:
case CMD_ilist:
case CMD_dlist:
case CMD_ijump:
case CMD_djump:
case CMD_isplit:
case CMD_dsplit:
arg = skipwhite(skipdigits(arg)); /* skip count */
if (*arg == '/') /* Match regexp, not just whole words */
{
for (++arg; *arg && *arg != '/'; arg++)
if (*arg == '\\' && arg[1] != NUL)
arg++;
if (*arg)
{
arg = skipwhite(arg + 1);
/* Check for trailing illegal characters */
if (*arg && vim_strchr((char_u *)"|\"\n", *arg) == NULL)
expand_context = EXPAND_NOTHING;
else
return arg;
}
}
break;
#ifdef AUTOCMD
case CMD_autocmd:
return set_context_in_autocmd(arg, FALSE);
case CMD_doautocmd:
return set_context_in_autocmd(arg, TRUE);
#endif
case CMD_set:
set_context_in_set_cmd(arg);
break;
case CMD_stag:
case CMD_tag:
expand_context = EXPAND_TAGS;
expand_pattern = arg;
break;
case CMD_help:
expand_context = EXPAND_HELP;
expand_pattern = arg;
break;
case CMD_bdelete:
case CMD_bunload:
while ((expand_pattern = vim_strchr(arg, ' ')) != NULL)
arg = expand_pattern + 1;
case CMD_buffer:
case CMD_sbuffer:
expand_context = EXPAND_BUFFERS;
expand_pattern = arg;
break;
#ifdef USE_GUI
case CMD_menu: case CMD_noremenu: case CMD_unmenu:
case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu:
case CMD_vmenu: case CMD_vnoremenu: case CMD_vunmenu:
case CMD_imenu: case CMD_inoremenu: case CMD_iunmenu:
case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu:
return gui_set_context_in_menu_cmd(cmd, arg, forced);
break;
#endif
default:
break;
}
return NULL;
}
/*
* Do the expansion based on the global variables expand_context and
* expand_pattern -- webb.
*/
static int
ExpandFromContext(pat, num_file, file, files_only, options)
char_u *pat;
int *num_file;
char_u ***file;
int files_only;
int options;
{
regexp *prog;
int ret;
int i;
int count;
if (!expand_interactively || expand_context == EXPAND_FILES)
return ExpandWildCards(1, &pat, num_file, file, files_only,
(options & WILD_LIST_NOTFOUND));
else if (expand_context == EXPAND_DIRECTORIES)
{
if (ExpandWildCards(1, &pat, num_file, file, files_only,
(options & WILD_LIST_NOTFOUND)) == FAIL)
return FAIL;
count = 0;
for (i = 0; i < *num_file; i++)
if (mch_isdir((*file)[i]))
(*file)[count++] = (*file)[i];
else
vim_free((*file)[i]);
if (count == 0)
{
vim_free(*file);
*file = (char_u **)"";
*num_file = -1;
return FAIL;
}
*num_file = count;
return OK;
}
*file = (char_u **)"";
*num_file = 0;
if (expand_context == EXPAND_OLD_SETTING)
return ExpandOldSetting(num_file, file);
if (expand_context == EXPAND_HELP)
return find_help_tags(pat, num_file, file);
set_reg_ic(pat); /* set reg_ic according to p_ic, p_scs and pat */
#ifdef AUTOCMD
if (expand_context == EXPAND_EVENTS)
reg_ic = TRUE; /* always ignore case for events */
#endif
reg_magic = p_magic;
if (expand_context == EXPAND_BUFFERS)
return ExpandBufnames(pat, num_file, file, options);
prog = vim_regcomp(pat);
if (prog == NULL)
return FAIL;
if (expand_context == EXPAND_COMMANDS)
ret = ExpandCommands(prog, num_file, file);
else if (expand_context == EXPAND_SETTINGS ||
expand_context == EXPAND_BOOL_SETTINGS)
ret = ExpandSettings(prog, num_file, file);
else if (expand_context == EXPAND_TAGS)
ret = find_tags(NULL, prog, num_file, file, FALSE);
#ifdef AUTOCMD
else if (expand_context == EXPAND_EVENTS)
ret = ExpandEvents(prog, num_file, file);
#endif
#ifdef USE_GUI
else if (expand_context == EXPAND_MENUS)
ret = gui_ExpandMenuNames(prog, num_file, file);
#endif
else
ret = FAIL;
vim_free(prog);
return ret;
}
static int
ExpandCommands(prog, num_file, file)
regexp *prog;
int *num_file;
char_u ***file;
{
int cmdidx;
int count;
int round;
/*
* round == 1: Count the matches.
* round == 2: Save the matches into the array.
*/
for (round = 1; round <= 2; ++round)
{
count = 0;
for (cmdidx = 0; cmdidx < CMD_SIZE; cmdidx++)
if (vim_regexec(prog, cmdnames[cmdidx].cmd_name, TRUE))
{
if (round == 1)
count++;
else
(*file)[count++] = strsave(cmdnames[cmdidx].cmd_name);
}
if (round == 1)
{
*num_file = count;
if (count == 0 || (*file = (char_u **)
alloc((unsigned)(count * sizeof(char_u *)))) == NULL)
return FAIL;
}
}
return OK;
}
#ifdef VIMINFO
static char_u **viminfo_history[2] = {NULL, NULL};
static int viminfo_hisidx[2] = {0, 0};
static int viminfo_hislen = 0;
static int viminfo_add_at_front = FALSE;
void
prepare_viminfo_history(len)
int len;
{
int i;
int num;
int type;
init_history();
viminfo_add_at_front = (len != 0);
if (len > hislen)
len = hislen;
for (type = 0; type <= 1; ++type)
{
/* If there are more spaces available than we request, then fill them
* up */
for (i = 0, num = 0; i < hislen; i++)
if (history[type][i] == NULL)
num++;
if (num > len)
len = num;
viminfo_hisidx[type] = 0;
if (len <= 0)
viminfo_history[type] = NULL;
else
viminfo_history[type] = (char_u **)lalloc(len * sizeof(char_u *),
FALSE);
}
viminfo_hislen = len;
if (viminfo_history[0] == NULL || viminfo_history[1] == NULL)
viminfo_hislen = 0;
}
int
read_viminfo_history(line, fp)
char_u *line;
FILE *fp;
{
int type;
type = (line[0] == ':' ? 0 : 1);
if (viminfo_hisidx[type] != viminfo_hislen)
{
viminfo_readstring(line);
if (!is_in_history(type, line + 1, viminfo_add_at_front))
viminfo_history[type][viminfo_hisidx[type]++] = strsave(line + 1);
}
return vim_fgets(line, LSIZE, fp);
}
void
finish_viminfo_history()
{
int idx;
int i;
int type;
for (type = 0; type <= 1; ++type)
{
if (history[type] == NULL)
return;
idx = hisidx[type] + viminfo_hisidx[type];
if (idx >= hislen)
idx -= hislen;
if (viminfo_add_at_front)
hisidx[type] = idx;
else
{
if (hisidx[type] == -1)
hisidx[type] = hislen - 1;
do
{
if (history[type][idx] != NULL)
break;
if (++idx == hislen)
idx = 0;
} while (idx != hisidx[type]);
if (idx != hisidx[type] && --idx < 0)
idx = hislen - 1;
}
for (i = 0; i < viminfo_hisidx[type]; i++)
{
history[type][idx] = viminfo_history[type][i];
if (--idx < 0)
idx = hislen - 1;
}
vim_free(viminfo_history[type]);
viminfo_history[type] = NULL;
}
}
void
write_viminfo_history(fp)
FILE *fp;
{
int i;
int type;
int num_saved;
init_history();
if (hislen == 0)
return;
for (type = 0; type <= 1; ++type)
{
num_saved = get_viminfo_parameter(type == 0 ? ':' : '/');
if (num_saved == 0)
continue;
if (num_saved < 0) /* Use default */
num_saved = hislen;
fprintf(fp, "\n# %s History (newest to oldest):\n",
type == 0 ? "Command Line" : "Search String");
if (num_saved > hislen)
num_saved = hislen;
i = hisidx[type];
if (i >= 0)
while (num_saved--)
{
if (history[type][i] != NULL)
{
putc(type == 0 ? ':' : '?', fp);
viminfo_writestring(fp, history[type][i]);
}
if (--i < 0)
i = hislen - 1;
}
}
}
#endif /* VIMINFO */